<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Enoch&apos;s Blog</title><description>Conversations on humans and computers</description><link>https://enochchau.com/</link><item><title>Lord of Agents, Master of None</title><link>https://enochchau.com/blog/2026/lord-of-agents-master-of-none/</link><guid isPermaLink="true">https://enochchau.com/blog/2026/lord-of-agents-master-of-none/</guid><description>&lt;p&gt;Agentic coding is to human software development like a word processor is to handwriting.
You can achieve similar output in half the time, but the gains of agentic
development are exponential compared to the linear gains of typing versus handwriting.
If you can see the path from A to B, the Agent can build you the car and pave the road
for you to drive there in a few hours where a human would have to manually toil away for several weeks.
For 90% of software development tasks, this is the right choice, but there remains that
final 10% that divides the revenue-generating potential of software from the free (as in freedom)
artistic pursuit of software.&lt;/p&gt;
&lt;p&gt;Software is a means to an end.
I use it to create products that are sold to customers, which generate revenue that
pays my salary and I get to go home to a well-furnished apartment and eat three square meals a day.
I don’t feel ownership over the code I write at work; it existed before me and
dozens of hands have molded it over the course of its life.
I follow the same language choices, linter rules, formatting rules, object-oriented
paradigm, build tools, frameworks, and infrastructure that my company has imposed upon me.
Sure, I could go make a big stink to write a service in Lisp,
but that’s counterproductive to the goal of the job which is to ship the damn product
and go home.
To that end, agentic coding is perfect because it helps me get there that much faster.&lt;/p&gt;
&lt;p&gt;Already, we can all run our own mini team of Agents in order to write lots and lots of code really quickly.
With this level of orchestration across a whole team, we could be committing hundreds of pull requests a day.
The bottleneck is no longer writing the code; instead, it is the human reviewer having to check the parts that the Agent cannot.
The problem is, how does the human know that what the Agent is doing is correct?&lt;/p&gt;
&lt;p&gt;Oftentimes, I will give a rubber stamp of approval to pull requests
that I don’t have enough context on to give a good review since I don’t want to block my coworker.
Plus, it’s not really up to the reviewer to catch bugs; the responsibility still falls
on the original author.
Now, with Agents writing the code, the original author is no longer the human and sometimes
the human whose name is on the pull request doesn’t have full context on the problem they’re working on.
This is particularly prevalent for junior engineers but can also affect anyone new to
the codebase or the team.&lt;/p&gt;
&lt;p&gt;In school, I had to put in the reps to get good grades.
I had to do the homework problems, bang my head against the wall, ask questions, and work with
my peers to develop my skills enough to pass the final exam.
The same thing is true for learning anything; I have to spend enough time with the
subject matter to develop my own understanding.
&lt;a href=&quot;https://www.anthropic.com/research/AI-assistance-coding-skills&quot;&gt;Software development is no different.&lt;/a&gt;
I started off web development with raw HTML before moving to jQuery and PHP, eventually
landing my first job using React with Node.js.
I had to put in the reps to learn what I know now.
I’m far along enough in my career now that I have my coding fundamentals down but
I am still developing my intuition on what makes a good product.&lt;/p&gt;
&lt;p&gt;So much can be delegated to the Agent now. I don’t even have to read the tickets anymore.
With enough MCP and API integrations, I can have the Agent create its own prompt by
reading the Linear ticket and asking follow-up questions through Slack.
Then it’ll code the solution, write tests to test the solution, then post the pull request without any intervention from me.
If I’m not in the loop at all, how can I ever hope to spend enough time with the problem
to develop intuition for what is correct?&lt;/p&gt;
&lt;p&gt;Conversely, is asking an Agent questions about the code really that different from
asking my coworker to explain something to me?
That comes down to a matter of context.
Does the limit of contextual knowledge start and stop in the codebase or is there something more that exists outside the grasp
of the Agent that only my coworker can explain to me?
Maybe there’s an edge case or specific design decision that is not clear to the Agent
or, even worse, it hallucinates and gives me bogus information.&lt;/p&gt;
&lt;p&gt;No one is forcing me to hand over my agency to the Agent.
Unfortunately, there’s limited time in a day.
Once my boss starts to expect the speed of output that only an Agent can deliver,
it’s too little too late to reverse course.
I’ve lost the opportunity to spend a lot of time with the code to learn things gradually.
Now I must leverage the Agent even more to ask it questions about the codebase
instead of doing the hard work of actually reading and tracing the code to draw my own
inferences and understanding.&lt;/p&gt;
&lt;p&gt;When I stopped speaking Chinese with my mother every day, my ability to recall
started to degrade.
I can feel myself struggling to find the words when I talk to her now.
The same is true with software development.
When I first started using Cursor, I felt like it could read my mind with its AI tab completion.
But when I turned off my work computer for the weekend and came back to
Neovim to edit this website, I realized that I was struggling to write basic CSS without
Cursor’s AI tab completion.&lt;/p&gt;
&lt;p&gt;I have to remind myself to treat Agents as tools to prevent myself from becoming overly reliant.
It’s up to me to slow down so that I can build a strong foundational understanding
of the problem space, the technology, and how it fits together in the product.
None of that is guaranteed to happen for free anymore.
It’s hard to keep up.
At the end of a long day, when I still see a mountain of tasks to finish, when I
have a deadline looming on the horizon, when I just want a bit of room to breathe;
in those moments of weakness, it’s easy to let the Agent run amok.
Maybe it’s correct 90% of the time, and maybe that’s good enough.
What scares me, though, is that I am not certain it is correct.
At least when I write the code, even if it’s buggy, I can be certain that, at the
time, I believed it was correct.
So while I may no longer be writing the code myself, I have to force myself
to take the time to prove the Agent’s correctness.&lt;/p&gt;
&lt;p&gt;There is a part of me that still yearns for the tactile feel of writing my own code with a keyboard.
I love my side projects.
I get to experiment with different tooling, different paradigms, and different languages.
I love chasing down novel ideas and experimenting to learn or just for the joy
of writing well-thought-out programs.
There was a time before all the Agents that I would do this at work too, but
that time has passed now.&lt;/p&gt;
&lt;p&gt;Agentic coding is like the fashion industry.
There’s Shein, just pumping out trends, as fast as fast fashion gets.
Then there’s Evan Kinori, hand-crafting every piece, focusing on the materials, slow
fashion at its finest.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I think software and clothes are just a means to an end, but I respect that
for some people, the means is the end.
&lt;em&gt;- Nathan Chau&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The slow fashion equivalent for the software world would be the open source folks.
They build slowly, creating reliable systems that must work.
They’re not beholden to the timeline of a company and the expected returns of its shareholders.
While not completely shielded from Agents, and even finding benefit in using Agents,
they can &lt;a href=&quot;https://github.com/ghostty-org/ghostty/blob/main/CONTRIBUTING.md#ai-usage&quot;&gt;scope their policy&lt;/a&gt;
to prevent the recklessness that AI can bring upon an organization.&lt;/p&gt;
&lt;p&gt;Lately, product managers, designers, and even support teams have all started to contribute code.
They’ve always hated the bottleneck of engineering time and now there’s a tool to circumvent that.
The engineers don’t mind either since it lets them focus on more interesting tasks than having to adjust the padding
by two pixels or change the color of the text.
At the end of the day, the engineers still have to review the code and correct any AI slop that non-engineers create.
As this paradigm proliferates, the pull requests from product managers and designers
will start to get bigger and bigger until the engineers are all but janitors
for the AI slop that the rest of the organization outputs.&lt;/p&gt;
&lt;p&gt;Maybe someday, perhaps someday soon, the Agents will be good enough that none of this will be a problem anymore.
The code will write itself and be perfect.
The Agent will manage its own orchestration; we will simply assign it tickets and it will create the perfect system.
Perhaps we will have to build many, many more data centers to see that day come and face
the environmental consequences.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My agents drank more water than me today.
&lt;em&gt;- Michael Zhao&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then engineers will really be obsolete, or perhaps move into some other role that
the Agent still can’t do, and keep moving roles as our numbers dwindle.
Is it stupid to think that there is a unique human essence that cannot be replicated
by machines when we are inherently creating things for the machine?
Does that human essence even have to come from someone who understands computers?
Does anyone actually even understand the inner workings of an LLM’s reasoning?
Perhaps this is the great democratization of technology and there really is no
artisanal slow fashion equivalent for software and all this pining for a simpler
time will forever be forlorn.
But I’m hopeful for the future.
Prometheus gave man fire and Google gave man &lt;a href=&quot;https://arxiv.org/abs/1706.03762&quot;&gt;“Attention Is All You Need”&lt;/a&gt;.
Fire can burn but at the same time, it gives us light and provides us warmth.
Some will be burned but the march of progress continues.&lt;/p&gt;</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate></item><item><title>Rage Against the Dying of the Light (Yosemite)</title><link>https://enochchau.com/blog/2026/dying-of-the-light/</link><guid isPermaLink="true">https://enochchau.com/blog/2026/dying-of-the-light/</guid><description>&lt;p&gt;Yosemite bouldering again.
First time in a few years now. Last time was probably with Paul, Paul, and Brando.&lt;/p&gt;
&lt;p&gt;Kodai forgot his puffy at the B&amp;amp;B in the morning.
He realized it just as we were rolling up to Camp 4.
We made our way towards the Wine Boulder, passing Midnight Lightning on the way up.
When we rolled up, there was a dude trying Dominator, Jerry Moffatt’s legendary climb.
We took a look around and chatted with him a bit.&lt;/p&gt;
&lt;p&gt;“Hey dude, do you know where Dying of the Light is? Like the one on the cover
of the guidebook?”&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;“Yeah, it’s at…&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; I just asked some other guy the other day and that’s what he
told me.”&lt;/p&gt;
&lt;p&gt;I turned to Kodai, “Dude, we should try it out today.”&lt;/p&gt;
&lt;p&gt;“Yeah man, it probably has one of the best climbing environments in the world.
Like even if you took El Cap out of the background.”&lt;/p&gt;
&lt;p&gt;“Does it climb well?”&lt;/p&gt;
&lt;p&gt;“Eh, yeah, it’s good,” he said hesitantly.&lt;/p&gt;
&lt;p&gt;“Oh by the way, what’s your name? I’m Enoch.”&lt;/p&gt;
&lt;p&gt;“I’m Forest.”&lt;/p&gt;
&lt;p&gt;We chatted a bit more. Forest was dirtbagging in his van, driving between
Bishop and Yosemite.
A large snowstorm had blown him here from Bishop.&lt;/p&gt;
&lt;p&gt;After warming up, we projected Initial Friction for a while.
I honestly thought it wouldn’t be that hard since Robinson’s Rubber Tester isn’t
that hard but damn, Initial Friction is on another level of trust your feet and
hope you don’t die.
It even took Forest a few tries, and he had already done it a few years back.&lt;/p&gt;
&lt;p&gt;It was already 2 p.m., and this late in the year, the sun was setting at 5 p.m., so we started packing up
to go on a little adventure to the top of a hill.&lt;/p&gt;
&lt;img src=&quot;/_astro/hiking-up.Ds_6vUCr_Z13xOSm.webp&quot; alt=&quot;hiking up&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;So we hiked up that hill; we even got a bit lost on the way up.
Through the forest, past looming boulders.
Hiking, scrambling, hiking, and hiking some more.&lt;/p&gt;
&lt;p&gt;And finally, we made it to the top.&lt;/p&gt;
&lt;img src=&quot;/_astro/pulling-on-dying-of-the-light.Bs19pYDk_ZcQMuO.webp&quot; alt=&quot;pulling on dying of the light&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;602&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;So dog-tired, but damn, the view was to die for.
I felt like I was dying as my legs screamed; dropping my pads provided a slight reprieve.
But it was worth it.
El Cap, large and looming in the distance, Yosemite Falls behind us.&lt;/p&gt;
&lt;p&gt;And so we begin.&lt;/p&gt;
&lt;p&gt;Setup the pads, start trying the moves.
Feeling stiff, gotta warm back up, and damn, the landing kinda sucks.
Try the top out, kinda spicy but not too hard.
Take a break, give a spot, take some photos.
Getting warm, finding beta, making links.
We were running out of time, racing the sun down.
Dammit, I didn’t want to get stuck on the top of this hill in the dark.&lt;/p&gt;
&lt;p&gt;Kodai sends, and I can feel the pressure building.
So I burn and burn and burn again, but I’m not resting and I’m not sending and
the sun is already disappearing under the horizon.
But Pitbull has my back and we’re gonna meet him at the Hotel Room Service.
And I’m sending, and we’re having the Time of Our Lives.
And we’re packing it up, but the sun is already down.
And the sky is dark, and we’re probably fucked.&lt;/p&gt;
&lt;p&gt;We took it slow on the hike back down, ambling through the dark, trying
to find whatever waypoints we could.
At one point, Kodai was behind me and he froze.&lt;/p&gt;
&lt;p&gt;“My right lens just popped out.”&lt;/p&gt;
&lt;p&gt;“Dude, no way,” I turned to yell, “Yo Nate, wait a sec.”&lt;/p&gt;
&lt;p&gt;We took a good fifteen minutes looking around on the ground, but we did find it
in the end.
And then we got lost again.
And a few more times before we finally made it back to the trail.
From boulder to car, it was one long hour of stumbling around in the dark.&lt;/p&gt;
&lt;p&gt;I looked up and I could see the stars.&lt;/p&gt;
&lt;img src=&quot;/_astro/yosemite-stars.D8q6flvj_Z1xPaUg.webp&quot; alt=&quot;yosemite stars&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;image&quot;&gt; 
&lt;div data-astro-cid-aomsn35f&gt; &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/vRID0IMAIEE?si=04an6cqoKpQiQkUL&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen class=&quot;astro-aomsn35f&quot; data-astro-cid-aomsn35f&gt;&lt;/iframe&gt; &lt;/div&gt; 
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;It’s not in the guidebook, “in the spirit of discovery.” An adventure waiting to happen, brought to you by James Lucas. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;In the spirit of the guidebook, I’m gatekeeping the location. &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><pubDate>Thu, 08 Jan 2026 00:00:00 GMT</pubDate></item><item><title>House Warming (Becoming the Barista)</title><link>https://enochchau.com/blog/2025/house-warming/house-warming/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/house-warming/house-warming/</guid><description>&lt;p&gt;We had a house warming party a few weeks back.
Finally, a chance to show off the space we’d been building out for the past
month.
Invite all the guests, her friends and mine, maybe some overlap.
Make sure we invite enough people but not too many, we only have 800 sqft to work
with.&lt;/p&gt;
&lt;p&gt;I wanted to do a home bar.
We’re starting at 2pm and no one’s really gonna show up until 3pm, so I’m
doing a menu of alcoholic or non-alcoholic, decaf or caffeinated, and sparkling water
for those healthier than thou.&lt;/p&gt;
&lt;h1 id=&quot;our-menu-for-the-day&quot;&gt;Our Menu for the Day&lt;/h1&gt;
&lt;img src=&quot;/_astro/homebar-menu.sHaRFG54_Z1kElgn.webp&quot; alt=&quot;homebar menu&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;596&quot; class=&quot;image&quot;&gt; 
&lt;h2 id=&quot;aeropress-yuzu-tonic&quot;&gt;AeroPress Yuzu Tonic&lt;/h2&gt;
&lt;p&gt;A spin on the classic espresso tonic.
With my limited gear, I brewed an AeroPress espresso with 18g of coffee to 90g of water
on an espresso grind using the inversion method.
Vigorously stir the “espresso” with a table spoon of Yuzu Tea Jam.
Then fill a glass with ice and sparkling water before topping with the espresso mixture,
maybe you’ll get some beautiful liquid stratification.&lt;/p&gt;
&lt;p&gt;Perhaps the most labor-intensive drink, requiring about five steps, but I could autopilot
through it by the end of the party while keeping a conversation going.&lt;/p&gt;
&lt;h2 id=&quot;v60-pour-over-beans-from-sey&quot;&gt;V60 Pour Over (Beans from &lt;a href=&quot;https://www.seycoffee.com/&quot;&gt;Sey&lt;/a&gt;)&lt;/h2&gt;
&lt;p&gt;Just a classic V60 pour over with delicious beans.
No one ordered this, which makes sense, because who’s drinking full cups of hot coffee
on a hot day when tomorrow is a Monday and we all need good sleep to beat the Sunday scaries.&lt;/p&gt;
&lt;h2 id=&quot;blood-orange-mimosa&quot;&gt;Blood Orange Mimosa&lt;/h2&gt;
&lt;p&gt;Trader Joe’s Blood Orange Soda mixed with either a red, white, or bubbly wine.
Some people got to choose their wine. If I forgot to ask, I’d default to bubbly.&lt;/p&gt;
&lt;h2 id=&quot;rum-high-ball&quot;&gt;Rum High Ball&lt;/h2&gt;
&lt;p&gt;One Shot of rum topped with ice and sparkling water, a classic drink.&lt;/p&gt;
&lt;p&gt;We ran out of rum about an hour in but Nate brought half a bottle of gin and that
lasted us till the end.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;As people trickled in, I’d point out the menu and tell them to come ask me
for a drink.
I’d write their name on the cup, a nice way to remember a new name, then
get about making their drink.
A few years back, I remember having a waiter at White Horse in Salt Lake City who could memorize
our entire table of eight’s drink and food orders, so I gave my memory a bit of a workout, baby weights,
only four menu items to remember.&lt;/p&gt;
&lt;p&gt;But we quickly added a new off menu drink when Steven came up and asked me
for a “frozen margarita”.
We had a few lemons in the fridge so I whipped up what I could:
some lemon juice, a spoonful of Yuzu Tea Jam, mixed with Rum and topped with ice and
sparkling water.
Pretty quickly after I made his drink, I realized what an excellent addition this could be.
Maybe without the lemon, too much effort to cut and squeeze but a stock Yuzu Highball
was a delicious addition.
Unofficially official, I’d mention it offhand to anyone getting a drink after him.&lt;/p&gt;
&lt;p&gt;I did realize after the fact that we didn’t provide water and I hope no one got too thirsty.
Next time, I’ll leave out une carafe d’eau for self-service.
I also hope guests didn’t feel overbearing asking for multiple drinks.
It’s just more fun for me.&lt;/p&gt;
&lt;p&gt;Some people were old friends, some people were new friends, but everyone
had to come up to the bar so I got to chat a bit with everyone.
It also kept me busy; I’m not always the most social person, so it was an excuse
to be social and an excuse to be engaged.&lt;/p&gt;</description><pubDate>Sun, 09 Nov 2025 00:00:00 GMT</pubDate></item><item><title>Running Small Teams</title><link>https://enochchau.com/blog/2025/running-small-teams/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/running-small-teams/</guid><description>&lt;p&gt;I recently ended my tenure at &lt;a href=&quot;https://welcome.gatsby.events/&quot;&gt;Gatsby&lt;/a&gt; after four years.
Throughout that time, I went from being a junior engineer with less than a year of
professional experience to a senior member of the team, responsible for multiple
product surfaces. I’ve seen many projects through to completion and into maintenance, and have felt firsthand
the results of my mistakes. Throughout this time, I’ve picked up some lessons about how a small team should be run.&lt;/p&gt;
&lt;p&gt;Three people impacted my understanding of team culture the most: our co-founder Michael, senior engineer Hugh, and product designer Andy.
Each of them had their own styles of working that at times clashed with one another.
While I don’t agree with all of their approaches, I used the culture they built to formulate
my own understanding.&lt;/p&gt;
&lt;p&gt;Michael and Hugh were the antithesis of each other.
Michael’s philosophy was quick and dirty: do what the customer needs as quickly
as possible. Don’t spend too much time on planning; execution is more important.
Hugh was more process-heavy. He pushed for daily stand-ups, sprint planning, and
design docs. While this made sure everyone was on the same page, it also meant that everything
took longer to finish.&lt;/p&gt;
&lt;p&gt;I mostly agree with Michael: small teams don’t need a lot of process.
Process is only as important as the increase in efficiency it provides by improving communication.
Why spend time in meetings when there are only two to three people working together who
can talk to each other directly?
Daily stand-ups are useful but should be limited to people who work together on the same team.
For example, I don’t need to hear about what sales is doing, but I do need to keep track
of what my engineering teammates are doing, or if there is an update to the design or product
specification.
The rule of thumb is: don’t waste people’s time.&lt;/p&gt;
&lt;p&gt;Hugh wanted to be an engineering manager, but small teams don’t need engineering managers.
What you do need is a single point of contact between engineering and product, such as an engineering lead.
The lead can understand the current progress of the engineering team and translate this
into accurate timelines for the product team.
Individuals should still be able to go around the engineering lead when appropriate.
For example, the designer should be able to work directly with the frontend engineer
to tweak the user interface.
Nevertheless, the lead is the primary point of contact for other stakeholders.&lt;/p&gt;
&lt;p&gt;Andy liked to ideate: he had engineers create demos, then he would tweak and sometimes
overhaul his original designs. This resulted in longer development cycles and
“wasted” engineering hours but usually ended up with a better, more polished UX.&lt;/p&gt;
&lt;p&gt;I wouldn’t spend too much time on initial designs.
Get the product working and ship it to users.
If you’re afraid of releasing an unpolished product, find a group of core users
who are willing to beta test.
You should also test internally, dogfood, and tweak, but don’t spend too much time polishing.
Most users don’t care as much about polish as engineers and designers do.
Using your own product is the best way to make a better product.&lt;/p&gt;
&lt;p&gt;To recap, small teams only need as much process as is necessary to communicate effectively.
Individuals should work directly with each other when it makes sense, but there
still needs to be a single point of contact for other stakeholders.
Don’t spend too much time tweaking the product or trying to cover every use case;
using the product is the best way to find its deficiencies.&lt;/p&gt;
&lt;p&gt;These are effective ideas for running a small team, but they also need to be paired
with guidelines to avoid burnout.
A lack of process can create murky job responsibilities and uncertainty around job performance.
In turn, I would tie the performance of the company directly to my own job performance.
Working more doesn’t always lead to burnout; burnout is a combination of long hours with meaningless work.
Individuals need to be excited and feel like they’re building toward something bigger.
Their ideas need to be heard, even bad ones, and considered, even if they are ultimately never implemented.
This can be done with tact and professionalism, detaching emotions from ideas and focusing
on what actually makes the product better.
Find people who have low egos and can give and take criticism freely.
Talk to people in person. We can communicate much more with our body language and facial expressions than
we can through text.&lt;/p&gt;
&lt;p&gt;Finally, keep your team small for as long as you can.
Many startup founders have an itch to play house, to build a team and act like they’re a “real company.”
Only hire for what you need to meet your next goal.
Don’t waste time and money on preemptively hiring people you don’t currently need.
It’s like the engineering principle &lt;a href=&quot;https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it&quot;&gt;YAGNI&lt;/a&gt; (ya ain’t gonna need it).&lt;/p&gt;</description><pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate></item><item><title>Interview Retrospective</title><link>https://enochchau.com/blog/2025/interview-retrospective/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/interview-retrospective/</guid><description>&lt;p&gt;After visiting Seattle in July, I noticed that many of my college friends working in big tech had bought or were buying homes.
Seattle is slightly cheaper than San Francisco but it gave me hope that maybe someday I could save up enough money for home ownership.
If only I could income max over the next few years.
Around this time, I also started getting a bunch of recruiter emails,
probably something to do with the improving economic situation in the tech and Venture Capital (VC) sectors.
As a result, over the last month and a half, I decided to take every single recruiter call
and do every single interview with the goal to use them primarily as practice, but also just
to test the water.&lt;/p&gt;
&lt;p&gt;First, I emailed back every recruiter that had emailed me in the months of June and July to set up initial calls.
Some of them got back to me, some of them ghosted me.
The worst one was one who tried to reschedule twice before ghosting me.
These were all third party recruiters who shopped my resume around to different startups,
some of which turned me down, others decided to move forward.
Besides the external recruiters, I also reached out to the Director of Engineering at &lt;a href=&quot;https://www.fermatcommerce.com/&quot;&gt;Fermat&lt;/a&gt;
since I went to their Series C party through an invitation from an ex-coworker.
I was also contacted by the internal recruiter for &lt;a href=&quot;https://imbue.com/&quot;&gt;Imbue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These initial screenings helped me to formulate a script as well as organize my motivations for seeking a new job.
Basically, I got really good at bullshitting people to make myself sound like I was the best fit for their company.
The goal at this point was to get through the initial screen so I could get practice on technical interviews.&lt;/p&gt;
&lt;p&gt;Around this time, I also started doing technical interview prep.
I replaced my video game time with &lt;a href=&quot;https://leetcode.com/&quot;&gt;LeetCode&lt;/a&gt; (LC) practice.
I replaced my TikTok scrolling with System Design YouTube shorts.
I was surprised by how much easier it is to learn and relearn computer science fundamentals today in the age of short form content.
I may have learned more in breadth of system design from YouTube shorts than I ever did from working as a software engineer.&lt;/p&gt;
&lt;p&gt;For first-round technical screens, these were my experiences with each respective company:&lt;/p&gt;
&lt;h2 id=&quot;imbue&quot;&gt;Imbue&lt;/h2&gt;
&lt;p&gt;I was expected to set up my own local environment to conduct the interview.
No AI coding tools were allowed and I screen shared with a third party interview proctor.
It was a pretty standard leetcode style assessment.
I’m guessing that they would have given this as an online assessment (OA) before the age of LLMs.&lt;/p&gt;
&lt;h2 id=&quot;fermat&quot;&gt;Fermat&lt;/h2&gt;
&lt;p&gt;Similar to the Imbue interview, this was another LC medium, conducted on CoderPad
This was less like an OA since I did it with a member of their engineering team.&lt;/p&gt;
&lt;h2 id=&quot;roger-healthcare&quot;&gt;Roger Healthcare&lt;/h2&gt;
&lt;p&gt;I was given an example repo and told to implement some features within 1 hour.
These features were basically impossible to implement for a human within the given
time frame and the candidate was expected to vibe code.
This was a fun one and very reflective of their engineering culture as an AI first company.
It was pretty funny to feed the prompts into Copilot and just sit back and watch the clock tick down.
I eventually had to bring in the big guns with Claude Code to get it across the finish line.&lt;/p&gt;
&lt;h2 id=&quot;loop&quot;&gt;Loop&lt;/h2&gt;
&lt;p&gt;This was a phone call with one of the engineers along with CoderPad.
On the surface, it seemed to be a pretty generic LC medium question but later I realized
that I failed hard because I did not clarify constraints before starting on the solution.
The weird part was that it was a phone call and I could have easily googled or
used an LLM to solve the problem.&lt;/p&gt;
&lt;h2 id=&quot;stepwork&quot;&gt;Stepwork&lt;/h2&gt;
&lt;p&gt;While this was a first round technical assessment, it was also the only technical assessment.
It was conducted by a third party interviewer that &lt;a href=&quot;https://www.stepwork.com/&quot;&gt;Stepwork&lt;/a&gt;
had found through a VC connection.
It was split into three parts: coding, knowledge, and system design.
The coding portion tested basic programming concepts.
The knowledge portion felt like random trivia.
It was very strange because I was sitting in front of my computer and could have easily googled any of the questions.
The system design question was fairly high level without delving too deeply into specific technologies.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;I moved forward with all the companies but was rejected by Loop.
The &lt;a href=&quot;https://www.loop.com/&quot;&gt;Loop&lt;/a&gt; interview was a good experience because it highlighted my naivete
with respect to constraints on LC problems whereas with a real world problem, I would have been much more thorough.
Overall, most of the interviews seemed to take into account that candidates could use
AI to cheat on LC style questions.&lt;/p&gt;
&lt;p&gt;For second-round interviews, each company upped the ante with harder LC questions, system design,
and live coding.&lt;/p&gt;
&lt;h2 id=&quot;imbue-1&quot;&gt;Imbue&lt;/h2&gt;
&lt;p&gt;I was given three LC hard questions to choose from and then collaborated with one of their engineers to try to solve it.
This was very challenging and we discussed at length different methods to solve the problem.
We never reached the coding portion.&lt;/p&gt;
&lt;h2 id=&quot;fermat-1&quot;&gt;Fermat&lt;/h2&gt;
&lt;p&gt;This was a system design round.
I was asked to design a website builder and deployment solution for said websites.
I’ve worked on a website builder before so it was fun to delve into the parts
of a website builder and the infrastructure that I hadn’t really thought about before.&lt;/p&gt;
&lt;h2 id=&quot;roger-healthcare-1&quot;&gt;Roger Healthcare&lt;/h2&gt;
&lt;p&gt;I met virtually with one of the founders and was presented with some example code.
I was asked to implement simple rate limiting.
I read &lt;a href=&quot;https://smudge.ai/blog/ratelimit-algorithms&quot;&gt;a post&lt;/a&gt; on Hacker News about
different rate limiting algorithms before so I was well prepared.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Of these, I was rejected from &lt;a href=&quot;https://www.rogerhealthcare.com/&quot;&gt;Roger Healthcare&lt;/a&gt; and Imbue.
Imbue gave me detailed feedback about my interview and also offered me a second
chance which I eventually turned down.
Roger Healthcare sent me a generic rejection email but if I had to guess the reason, it
may have been something I said about vibe coding.&lt;/p&gt;
&lt;p&gt;I had one final round with Fermat where we reviewed my past projects.
After that, I had two weeks of radio silence before they finally responded
saying they had up-leveled the position I was applying for and removed me from the pipeline.
Seemed like a huge waste of time for both them and me but it was likely coming from the top down.&lt;/p&gt;
&lt;p&gt;Rejections are always hard to take but I appreciate Imbue for being transparent and
giving good feedback on what I could improve on.
Thankfully, I actually got rejection emails from all of them.
In the past, I’ve talked to startups who have ghosted me after one or two rounds.&lt;/p&gt;
&lt;p&gt;As for recruiters, there were two that I would like to highlight.&lt;/p&gt;
&lt;p&gt;Gabriel Nadj and Aaditya Jain at Employia did an excellent job with my initial screening.
Both are recruiters but Aaditya seemed to have a technical background so I could
go into more depth about my experience instead of giving a high-level overview as I normally
would to a non-technical person.&lt;/p&gt;
&lt;p&gt;Zen Mak at RallyWorks had a personal touch that I wasn’t used to but came to
greatly appreciate.
Typically, I try to distance myself from recruiters and interviewers as interviewing can be dehumanizing at times.
With this in mind, I was initially confused by Zen’s friendliness but it eventually overcame me.&lt;/p&gt;
&lt;p&gt;Throughout the past month and a half, I would sometimes wake up to my heart pounding with anxiety.
I was averaging 5 calls a week, sometimes multiple technical interviews back to back.
Talking to someone new everyday, turning on the charm, and having to constantly repeat the same introduction to the recruiter, and in subsequent rounds of interviewing was exhausting.
Technical interviews were also mentally draining, I would leave the interview feeling defeated, even if I did well.
My only reprieve was the climbing gym where I could disconnect from my work life and hang out with my friends.
Around this time, I was also searching for a new apartment.
The summer rental market was brutal.
I spent hundreds of dollars and received multiple rejections as the AI startup boom and Return to Office began jacking up rent across the city.
I realized that I didn’t have to do this to myself so I started winding down interviews.&lt;/p&gt;
&lt;p&gt;I’m still getting recruiter emails but I’ve stopped responding for the time being.
I got a lot of good practice with interviewing on both soft and hard skills.
I’ve also realized that my original goal of income maxing for home ownership was kind of pointless
and that a job is much more than the dollar amount it provides.&lt;/p&gt;
&lt;p&gt;I met my girlfriend’s uncle while we were in Seattle and he gave us a tour of his office.
He would smile and say “Hi” to all his coworkers as we walked through the halls.
We ended the tour on the rooftop overlooking Bellevue.
He remarked that he’d been working there for more than ten years and that he really loved his job.
I want a job like that, somewhere I can work and love to work for that long.&lt;/p&gt;</description><pubDate>Fri, 05 Sep 2025 00:00:00 GMT</pubDate></item><item><title>My Mother&apos;s Mother</title><link>https://enochchau.com/blog/2025/my-mothers-mother/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/my-mothers-mother/</guid><description>
&lt;div style=&quot;white-space:pre-line;&quot;&gt;&lt;p&gt;婆婆 died.
She was a hard-ass woman.
She raised three kids as a widow.
She was a widow for 50 years.
She never remarried.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;There was a story she liked to tell me that I also heard from my mom.
My grandmother had to go away for a week and during that time, my uncle tried smoking cigarettes.
When my grandma came home, my mom and aunt told on my uncle and my grandma beat his ass.
She would still hit them when they were teenagers, but that was a different time.&lt;/p&gt;
&lt;p&gt;My mom would also tell me another story about how stubborn my grandma was.
Mom was about to get kicked out of college for failing her classes. She had already appealed to the dean, but the decision stood.
But my grandma wouldn’t give up. She took a bus to the university, walked into the dean’s office, and demanded to speak with him.
She begged him to give my mother another chance, and somehow convinced him.
Mom ended up majoring in film.
She was a film producer in Taiwan before coming to the US.&lt;/p&gt;
&lt;p&gt;My grandmother was a seamstress, but she would never teach my mom to sew.
She wanted her children to have better careers, to have better lives.
Mom learned anyway and later, grandma helped mom make costumes for mom’s commercials.&lt;/p&gt;
&lt;p&gt;婆婆 was the only grandparent I had that I formed a real relationship with.
I could talk to her, hear her stories without translation, see her sense of humor.
She would also scold me from time to time, but she was always sweet to her grandchildren.
One time, she told me not to date around too much, to find a good girl to settle down with.
After all, she met two of my girlfriends.&lt;/p&gt;
&lt;p&gt;In 2019, I went to Taiwan with my ex and they met.
Grandma called a taxi and we went to get conveyor belt sushi in the food court of a Carrefour.
My grandma always liked going out for conveyor belt sushi, she would say that its cheap and good.
Back then, she was already wearing a back brace but she could still walk with a cane.
Those were the times I loved the most with her, when my Mom or Aunt or Uncle weren’t
around and it was just me and 婆婆.&lt;/p&gt;
&lt;img src=&quot;/_astro/me-and-grandma-at-carrefour.nzDQZdzD_1GVQ5X.webp&quot; alt=&quot;me and grandma at carrefour&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1330&quot; class=&quot;portrait&quot;&gt; 
&lt;p&gt;The last time I saw her was in 2024, I was in Taiwan for eleven days and I went
to see her twice on my own.
Once, she was sleeping, so I just waited around, held her hand, whispered to her
while she slept.
I didn’t really know what else to do, and I didn’t want to just leave.
The other time she was awake and in a good mood.
That’s when I asked her about World War II.&lt;/p&gt;
&lt;p&gt;She said she was too young to have learned Japanese in school during the colonial period,
but she had a lot of siblings, and her older brothers, my grand uncles, were old enough.
Two of them went off to fight for the Japanese during the war.&lt;/p&gt;
&lt;p&gt;After the war, and the subsequent Civil War, my grandfather came over to Taiwan with the KMT.
He was placed as a teacher in 九份 which was a good job at the time.
He was also a handsome man; all the girls in town wanted to marry him but somehow,
he ended up with my grandmother.
I think she was quite proud of that.&lt;/p&gt;
&lt;img src=&quot;/_astro/grandma-wedding.xBtMUOfG_zossN.webp&quot; alt=&quot;grandma&apos;s wedding photo&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1189&quot; class=&quot;portrait&quot;&gt; 
&lt;p&gt;Several years ago, maybe in 2014 or 2015, we had a big family trip to 九份 with my mom, 舅舅, 舅媽 and cousins.
That was my first time going and we stayed at a hotel owned by my relatives.
While we were walking down the old street, we ran into some of 婆婆’s friends from elementary school who still lived there.&lt;/p&gt;
&lt;p&gt;On a different trip to 九份, my mom took my dad, my brother and I to go look for mom’s old family home.
That’s the house my mom was born in.&lt;/p&gt;
&lt;p&gt;At some point, my grandfather got a promotion and became the principal of 新店國小.
He was given a traditional Japanese house behind the school to live in.
A few years later, he died in a motorcycle accident.
My grandmother got to keep the house, and that’s where my mom grew up.
I lived in that house too when I was a kid.
Maybe when I was eight years old, my mom took my brother and I to Taiwan and we stayed with my grandmother there.
All the rooms were connected by sliding doors.
We slept on futons on the floor.
She didn’t even have a shower, it was just a stone room with a drain and we washed ourselves with buckets.
I remember lying on the floor and watching One Piece on TV.&lt;/p&gt;
&lt;p&gt;When I was older, in high school, she had moved to an apartment.
After all those years, a developer wanted to buy the property.
Initially, she didn’t want to sell, but after some time she caved.
We thought they would build highrises but they never got around to demolishing the area.
The next time I was in Taiwan, my mom and I went back to check it out and the house was still there, in a dilapidated state but still standing.
We snuck around the back and went inside and it was just like how I remembered it.
Later, my cousin told me that the government wanted to turn it into a museum as part of the Japanese history of Taiwan, but that still hasn’t happened.
Typical bureaucracy.&lt;/p&gt;
&lt;img src=&quot;/_astro/old-house-with-mom.CF_qmURl_ZY07df.webp&quot; alt=&quot;inside the old house&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;809&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I also stayed at my grandma’s apartment a good deal.
Free housing I guess.
It was a studio apartment, with a shower that was filled with buckets, I guess some habits never die.
My mom would sleep with my grandma in her bed and I would sleep on the floor.
Those were hot summer nights in Taipei.
I still sleep with the AC off whenever I’m in Taiwan.
I find it comforting to be smothered in the humid heat, with just my stomach covered by the blanket.
My grandma’s bed had a wood slat topper.
I asked her about it and she said it was to keep cool.
It just didn’t look very comfortable.&lt;/p&gt;
&lt;p&gt;She would make me 牛肉麵.
That actually happened a few times.
In the summer of 2017, my brother and I both went to study Chinese at 師大.
She made us that beef noodle soup.
It’s a lighter broth, not the salty, heavy kind.
She also always had these vanilla ice cream sandwiches that had a cracker instead of a cookie.&lt;/p&gt;
&lt;p&gt;During that summer, I also got a mouth infection.
From where? I have no idea.
I called my mom and my mom arranged for my grandma to take me to see a doctor.
The doctor we went to was the same doctor my mom saw from when she was younger.
So my grandma took me to get checked up and I got prescribed medicine that we picked up next door.
It all came out to less than $40, I remember because I filed an insurance claim.&lt;/p&gt;
&lt;p&gt;She was a hard ass woman to my mom and her siblings, but she was very sweet to her grandchildren.
She got by all those years through her faith in God.
I’ve been to her church before.
It’s a lot like the Church I grew up in.
She used to get rides from the brothers and sisters that would help her out.
Later, when it was harder for her to walk, she’d go to a smaller church a block away from her apartment.&lt;/p&gt;
&lt;p&gt;The last time I saw her, she even gave me a Chinese Bible that had 注音符號 on it so that I could actually read it.
Initially, I didn’t really want to take it since it would take up luggage space but I took it anyway.
I don’t know if I’ll ever read it but I’ll always have it.
It’s a physical reminder of my grandmother’s unwavering faith in God.&lt;/p&gt;
&lt;p&gt;Before every meal, she would always pray.
She’d even scold us if we forgot to pray.
She would pray about everything, she had so much faith in God, and rightly so.
That’s how she got by as a widow with three children for fifty years.&lt;/p&gt;
&lt;p&gt;I’m sad I won’t see her anymore, whenever people used to ask why I was going to Taiwan I would say “Oh, I’m visiting my grandma”.
But now there’s no grandma to visit, instead she’ll always be with me.
She had Parkinson’s disease for years. I’m glad her pain has ended, and she can rest now with God.&lt;/p&gt;</description><pubDate>Mon, 25 Aug 2025 00:00:00 GMT</pubDate></item><item><title>Performance of Positioning for Windowing</title><link>https://enochchau.com/blog/2025/performance-of-positioning-for-windowing/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/performance-of-positioning-for-windowing/</guid><description>&lt;p&gt;&lt;em&gt;this post assumes knowledge of virtualization/windowing&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At &lt;a href=&quot;https://gatsby.events&quot;&gt;Gatsby&lt;/a&gt;, one of the projects I worked on a few years back was creating our
high performance guest list table component.
At that time, we used &lt;a href=&quot;https://github.com/bvaughn/react-window&quot;&gt;React Window&lt;/a&gt; as
an off the shelf solution in order to render thousands of guest rows in the table.
Since then, I’ve wanted to write my own windowing/virtualization library.&lt;/p&gt;
&lt;p&gt;Lately, I’ve been working on my project: &lt;a href=&quot;https://github.com/enochchau/windowing&quot;&gt;Windowing&lt;/a&gt;.
It’s a windowing demo for React that I hope to turn into a full fledged library.
I was largely inspired by React Window but I wanted to write a modern implementation.
React Window was written to be backwards compatible, with class components, my Windowing library
uses modern function components and hooks.
I’ve often always believed that the best way to understand something is to do it yourself.&lt;/p&gt;
&lt;p&gt;React Window uses &lt;code&gt;absolute&lt;/code&gt; positioning with &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; for positioning of cells within the grid.
My naive implementation for Windowing also used this same strategy.
But after building my library, I wanted to see what industry standard &lt;a href=&quot;https://www.ag-grid.com/&quot;&gt;AG Grid&lt;/a&gt;
does differently as their competitive advantage.
For positioning of columns, they do use &lt;code&gt;left&lt;/code&gt; but for rows, I noticed that they
use &lt;code&gt;transform: translateY&lt;/code&gt; for positioning.&lt;/p&gt;
&lt;p&gt;Transform translate has been known as a performance hack to offload CPU work to the GPU.
Using &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; for positioning requires CPU operations to calculate the
layout of the DOM but &lt;code&gt;transform: translate&lt;/code&gt; offloads some of the work to the GPU.
To verify, I did some benchmarking of my own.&lt;/p&gt;
&lt;p&gt;In the Firefox profiler, we can see that the CPU time is half when using translate vs. &lt;code&gt;top&lt;/code&gt; positioning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Top Positioning - Firefox - 964ms&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/top-firefox.DyU8IgTH_1fOQjF.webp&quot; alt=&quot;top positioning firefox&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;541&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;Translate Positioning - Firefox - 465ms&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/translate-firefox.BUCZUL6n_Z1oOt31.webp&quot; alt=&quot;translate positioning firefox&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;513&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;And in Chrome, we can see that the commit step is much faster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Top Positioning - Chrome - Commit 860μs&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/top-chrome.BktKN3cg_z26wd.webp&quot; alt=&quot;top positioning chrome&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;454&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;Translate Positioning - Chrome - Commit 36μs&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/translate-chrome.CwfAaPpI_Z1CbSpK.webp&quot; alt=&quot;translate positioning chrome&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;369&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;That’s almost 24x faster!&lt;/p&gt;
&lt;p&gt;Asking Gemini about the Commit step in Chrome’s performance tools:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A slow commit step can indicate issues on the main thread that are delaying the handover to the compositor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So a slow commit step means we’re spending more CPU time rather than offloading calculations to the GPU compositor.
A faster commit step means we’re doing less single threaded CPU work and can utilize the multi-core nature
of the GPU.
There’s actually a visual difference when scrolling with row/column overflow set to 0
(no additional rows and columns are rendered outside the window as a buffer).
When using &lt;code&gt;top&lt;/code&gt; positioning, the browser takes more CPU time to do DOM layout
calculations which delays rendering of HTML which results in more time seeing
white space when scrolling quickly before the HTML pops in.
When using &lt;code&gt;translate&lt;/code&gt; positioning, setting the buffer to 0 has almost no noticeable difference
to setting a buffer of 3 as the majority of the work is handed off to the GPU and the
CPU isn’t delayed in rendering HTML&lt;/p&gt;
&lt;p&gt;Checkout the &lt;a href=&quot;https://enochchau.github.io/windowing/&quot;&gt;demo for Windowing&lt;/a&gt;!&lt;/p&gt;</description><pubDate>Fri, 16 May 2025 00:00:00 GMT</pubDate></item><item><title>Menu Bar App Revisited</title><link>https://enochchau.com/blog/2025/menu-bar-app-revisted/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/menu-bar-app-revisted/</guid><description>&lt;p&gt;Previously, I wrote about making a &lt;a href=&quot;/blog/2025/hey-gemini-write-me-a-menu-bar-app&quot;&gt;MacOS Menu Bar app using Gemini&lt;/a&gt;.
In that post, I left a lot of loose ends left by the generated code that I never tidied up.
With a bit more time over the last few weeks, well, I tidied them up.&lt;/p&gt;
&lt;p&gt;First, I was using deprecated APIs. So I went back and updated those.
I went down the tried and true path, starting with the &lt;a href=&quot;https://developer.apple.com/documentation/appkit/nsworkspace/setdefaultapplication(at:toopenfileat:completion:)&quot;&gt;Apple Developer Docs&lt;/a&gt;
and then combing through various software forums to find examples on how to do
these things.
All culminating in an aptly named commit titled &lt;a href=&quot;https://github.com/enochchau/BrowserDefaults/commit/6f03308e5ce53d2d322035f616bd8054005cc319&quot;&gt;“refactor AI generated code”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, I wanted a prettier UI. During my initial search for an app that did
what I created, I found &lt;a href=&quot;https://sindresorhus.com/default-browser&quot;&gt;“Default Browser” created by Sindre Sorhus&lt;/a&gt;
but damn it if that &lt;button style=&quot;width:80px;height:30px;background:#2563eb;color:white;border-radius:8px;font-weight:bold;&quot;&gt;Buy&lt;/button&gt;
button didn’t discourage me from taking a closer look. But Sindre’s app has
icons and I wanted icons. This ended up being a fun conversation with Gemini
where it gave me examples using &lt;code&gt;NSworkspace&lt;/code&gt; to get an icon from a URL handler,
which I &lt;a href=&quot;https://github.com/enochchau/BrowserDefaults/commit/f3e1a6dfcfdbd3a2b55d4584a34ca3d1a5be7f54&quot;&gt;then implemented&lt;/a&gt;
as well as setting the main menu bar icon so I could tell, at a glance, what the
default was.&lt;/p&gt;

&lt;img src=&quot;/_astro/updated-menu-bar.RpQXPTSF_Z2qUiOW.webp&quot; alt=&quot;updated menu bar&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;313&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Around this time, Gemini 2.5 Pro was released and I wanted to retry the original prompt
with the new model. My own little way to benchmark these LLMs, you might say.
Gemini 2.5 Pro still didn’t account for changing the default browser from a different place in the OS
but it ended up implementing the UI using &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/menubarextra&quot;&gt;&lt;code&gt;MenuBarExtra&lt;/code&gt;&lt;/a&gt;.
That was new to me, all the previous responses I got had used &lt;code&gt;NSMenu&lt;/code&gt; and &lt;code&gt;NSMenuItem&lt;/code&gt;,
but that’s cool, that’s something new to learn.
I &lt;a href=&quot;https://github.com/enochchau/BrowserDefaults/commit/b2438c7f390b5952578a8bd2dc612c8bc1e63a97&quot;&gt;tried &lt;code&gt;MenuBarExtra&lt;/code&gt; out a bit&lt;/a&gt;, learning a bit of SwiftUI and a bit more of XCode.&lt;/p&gt;
&lt;p&gt;I’m liking MacOS development more now.
There’s not that many examples or tutorials so I ask Gemini a lot of questions to learn syntax and see examples.
I’ve found that using AI as a partner for learning feels like actual development instead
of throwing darts blindly.
I first learned to program watching YouTube tutorials and reading example blog posts,
not by reading documentation.
Now, as a seasoned programmer, I’m more comfortable reading the manual but some learning styles,
like seeing examples, just work better for me.
I need a little bit of inspiration.
And as always, you don’t know what you don’t know, and sometimes the AI chatbot
can tell you something new.&lt;/p&gt;
&lt;p&gt;Here’s a link to &lt;a href=&quot;https://github.com/enochchau/BrowserDefaults&quot;&gt;the project’s GitHub&lt;/a&gt;.&lt;/p&gt;</description><pubDate>Tue, 22 Apr 2025 00:00:00 GMT</pubDate></item><item><title>Hey Gemini, Write Me a Menu Bar App</title><link>https://enochchau.com/blog/2025/hey-gemini-write-me-a-menu-bar-app/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/hey-gemini-write-me-a-menu-bar-app/</guid><description>&lt;p&gt;I need to switch my default browser frequently.
On my MacBook, I use Chrome at work but I use Firefox as my personal browser.
I have some &lt;a href=&quot;https://github.com/enochchau/dotfiles/blob/1ed11c39b31a765f50a0a55d78b5bd251deb193f/roles/skhd/files/skhdrc&quot;&gt;hotkeys&lt;/a&gt; setup so I can quickly switch my browser when I clock out,
but sometimes I forget to switch it back in the morning and end up opening links
in the wrong browser.&lt;/p&gt;
&lt;p&gt;I want something more visual, like a small gui that I can check.
Maybe something that lives in OSX’s top menu bar so I can always see whats
active.
And maybe be able to change my default browser from there as well.&lt;/p&gt;
&lt;p&gt;The problem is I don’t know anything about making Mac apps.
But in the year 2025, that’s hardly an excuse to balk at a challenge when
we have nifty chatbots like ChatGPT and Gemini to write apps for use.
I chose Gemini, since I read somewhere that it’s model was the best at the time.&lt;/p&gt;
&lt;p&gt;My first few prompts were mostly for exploration:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;in MacOS, how can I programmatically change the default browser?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With which it responded with a bash script.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How can I do this using MacOS APIs?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now it’s giving me some Swift and Objective-C code, using &lt;code&gt;LSSetDefaultHandlerForURLScheme&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How can I create a toolbar widget that can do this for me with a UI menu?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The code I got back from this prompt used &lt;code&gt;AppDelegate&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Cocoa&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; CoreServices&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;@main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AppDelegate&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NSObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NSApplicationDelegate &lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; func&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; applicationDidFinishLaunching&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt; aNotification&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: Notification) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Create the status item&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        statusItem = NSStatusBar.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;system&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;statusItem&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;withLength&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: NSStatusItem.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;squareLength&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; button = statusItem.button {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            button.image = &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;NSImage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;systemSymbolName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;globe.americas&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;accessibilityDescription&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;Default Browser Switcher&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            button.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;action&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;#selector&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;showMenu&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(_:))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            button.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;target&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;self&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Build the menu&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; menu = &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;NSMenu&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So I created a new MacOS app project in XCode and pasted in the code.&lt;/p&gt;
&lt;p&gt;At this point, I had no idea what an App Delegate even was.
I tried pasting the code into the main file and deleting things, changing Info.plist,
but nothing I did would get it to run.
It was late at night at this point, so I shelved the idea.&lt;/p&gt;
&lt;p&gt;A few days later, I decided I should do some preliminary research into what I was doing.
I read up a bit on what the App Delegate is as well as the structure of a Swift app.
Armed with this knowledge I returned to Gemini.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How can I create a top menu bar item graphical application in MacOS using Swift and XCode that can switch default browsers for me?
I want the application to have a dropdown menu that shows the current default browser and allows me to change it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The code it gave me at this point, basically worked.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Cocoa&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;@main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; AppDelegate&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NSObject&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NSApplicationDelegate &lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    private&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; statusItem: NSStatusItem!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    private&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; currentDefaultBrowser: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; applicationDidFinishLaunching&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt; aNotification&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: Notification) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Create the menu bar item&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        statusItem = NSStatusBar.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;system&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;statusItem&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;withLength&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: NSStatusItem.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;variableLength&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Set the icon (optional)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; button = statusItem.button {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            button.image = &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;NSImage&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;systemSymbolName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;globe&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;accessibilityDescription&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;Switch Default Browser&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Build the menu&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;        constructMenu&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Get the initial default browser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;        updateCurrentDefaultBrowser&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; applicationWillTerminate&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt; aNotification&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: Notification) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        // Insert code here to tear down your application&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; constructMenu&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;      // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    @objc&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; func&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; changeDefaultBrowser&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt; sender&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: NSMenuItem) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; browserIdentifier = sender.representedObject as? &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;            let&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; url = &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;URL&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;http://example.com&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)! &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// Use a dummy URL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            LSSetDefaultHandlerForURLScheme&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;http&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; as CFString, browserIdentifier as CFString)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            LSSetDefaultHandlerForURLScheme&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;https&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; as CFString, browserIdentifier as CFString)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            updateCurrentDefaultBrowser&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;            constructMenu&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// Rebuild the menu to update the current browser display&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; updateCurrentDefaultBrowser&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;      // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There was only one issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The current browser is not being set correctly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After changing the browser default, the menu bar dropdown would not reflect the
up-to-date value for the current default browser.&lt;/p&gt;
&lt;p&gt;Gemini thought it was a problem with the &lt;code&gt;changeDefaultBrowser&lt;/code&gt; function above,
but pasting it’s new code made no difference.&lt;/p&gt;
&lt;p&gt;At this point, Gemini couldn’t really figure out my problem because it’s never
actually used OSX before.
What happens when you change your default browser is that
a pop-up window will ask you to confirm the switch.&lt;/p&gt;
&lt;img src=&quot;/_astro/chrome-to-firefox-browser-default-switch.DShIBvCz_Z23rymz.webp&quot; alt=&quot;switching browsers pop-up&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;857&quot; class=&quot;portrait&quot;&gt; 
&lt;p&gt;The default browser won’t actually change until the pop-up window is confirmed.
But the menu is already re-drawn by the time the pop-up is confirmed, so the
new browser default state isn’t captured in the updated menu.&lt;/p&gt;
&lt;p&gt;What we need is something like a notification or polling mechanism to make sure
the menu is up-to-date after the pop-up is confirmed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How can I have it listen to changes in the default browser from other places in the OS?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Gemini said to register the app in launch services:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You need to register your application to receive notifications for changes in default handlers for the “http” and “https” URL schemes. You can do this in your AppDelegate’s applicationDidFinishLaunching method.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which didn’t work and I wasn’t keen to dig deeper since it seemed to need
deeper knowledge of OSX APIs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What if i use polling instead of notification event?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well that worked.&lt;/p&gt;
&lt;p&gt;The rest of the prompting was to see if I could use non-deprecated APIs but
that didn’t work either on the first try.
&lt;a href=&quot;https://github.com/kerma/defaultbrowser&quot;&gt;&lt;code&gt;kerm/defaultbrowser&lt;/code&gt;&lt;/a&gt; uses the same
deprecated API, namely &lt;code&gt;LSCopyAllHandlersForURLScheme&lt;/code&gt;.
Even &lt;a href=&quot;https://github.com/jwbargsten/defbro&quot;&gt;&lt;code&gt;jwbargsten/defbro&lt;/code&gt;&lt;/a&gt;, a re-write
of &lt;code&gt;defaultbrowser&lt;/code&gt; in swift, still uses &lt;code&gt;LSCopyAllHandlersForURLScheme&lt;/code&gt;
so I decided to leave it as be for now.
Maybe a better chatbot in the future can fix this for me when the API is removed.&lt;/p&gt;
&lt;p&gt;You can see the source code here: &lt;a href=&quot;https://github.com/enochchau/BrowserDefaults&quot;&gt;&lt;code&gt;enochchau/BrowserDefaults&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;img src=&quot;/_astro/default-browser-menu-bar.CChlIlPI_Z1sMJNj.webp&quot; alt=&quot;default browser menu bar app&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;887&quot; class=&quot;portrait&quot;&gt; 
&lt;p&gt;I’m happy with what I was able to create but there’s a nagging feeling in the
back of my head that this was a missed opportunity.
I could have taken the time to learn more about OSX, Mac app development, and Swift
and I did learn a little bit, but not as much as if I figured out and wrote all the code
myself.
The thing is, if I hadn’t used Gemini to code the app, I’d probably end up using
something like &lt;a href=&quot;https://github.com/swiftbar/SwiftBar&quot;&gt;SwiftBar&lt;/a&gt; or &lt;a href=&quot;https://www.hammerspoon.org/&quot;&gt;Hammerspoon&lt;/a&gt;
as a simpler interface to OSX.
AI Chatbots may be a good replacement for some smaller low-code or no-code tools, but at least for now,
I don’t see it as a full replacement for software developers.&lt;/p&gt;</description><pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate></item><item><title>Slow Down</title><link>https://enochchau.com/blog/2025/slow-down/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/slow-down/</guid><description>&lt;p&gt;They were in a little open air refrigerator at the front of the conbini.&lt;/p&gt;
&lt;p&gt;“I saw these on TikTok, they’re good if you’re sick.”&lt;/p&gt;
&lt;p&gt;“What are they?”&lt;/p&gt;
&lt;p&gt;They were little glass bottles lined up in rows. Most had pictures of lemons
on them. They looked like they could be tasty, and vaguely medicinal.
I picked one up and deciphered ‘5000mg of Vitamin C’ from the little
bits of Katakana I could pick out.&lt;/p&gt;
&lt;p&gt;“I guess I’ll get this one, but what’s the difference between these two?”&lt;/p&gt;
&lt;p&gt;The one I was holding had a green label with a lemon on it.
The other bottle had a blue label with a lemon on it.
But I could feel the girls behind me waiting for us to move.
They didn’t say anything. They just stood silently there behind us, waiting for us to move of our own volition.
I watched them out of the corner of my eye as I walked into the next aisle.
They were young, probably around my age, dressed to go out. They both
picked up two bottles of the blue label lemon.&lt;/p&gt;
&lt;p&gt;‘Dang I wonder why they picked those’ I thought to myself.
So I circled back around to translate the labels, this time with my phone, only to decipher something about
‘a drink to reduce swelling caused by alcohol’.
‘Dang I guess they’re gonna get fucked up tonight, I wonder if these actually work’
but I was feeling a little too sick to try it out. I stuck with my green label
5000mg of Vitamin C. Probably better in the moment than a magical hang over cure.&lt;/p&gt;
&lt;p&gt;But I couldn’t shake that feeling of them waiting behind me. No すみません,
just waiting silently. Even when getting off a crowded train, no すみません, just waiting
silently, with maybe a light shove if you were really clueless.&lt;/p&gt;
&lt;p&gt;Back home, when I’m in the grocery store, I like to do this thing I’m going to call wading.
I’m wading between the people, moving slowly, flowing between the people around me instead of “Excuse me!”-ing my way through.
Especially with the mini green carts at Whole Foods with slick bearings, I feel like I’m gliding.
Everyone at the grocery store is
there for the same reason. We’re all trying to get to that next thing on our shopping list.
And it’s America, the land of plenty, no need to run to get the last carton of eggs
(although that seems to be different now).&lt;/p&gt;
&lt;p&gt;I like the calming slowness of such a hectic society as the big Japanese cities like Osaka and Tokyo.
It feels like I’m wading, not just at the grocery store, but everywhere, at the train stations, at the conbini,
at the department stores, getting off the train.
With maybe ten times the number of people as I ever encountered in New York,
where some people won’t even “Excuse me!” as they push past you.
But to each their own.
Since I’m also the one kicking rats down the street trying to catch the F train.&lt;/p&gt;</description><pubDate>Fri, 28 Mar 2025 00:00:00 GMT</pubDate></item><item><title>Optimal Filter Search Sort List</title><link>https://enochchau.com/blog/2025/optimal-filter-sort-search-list/</link><guid isPermaLink="true">https://enochchau.com/blog/2025/optimal-filter-sort-search-list/</guid><description>&lt;p&gt;A problem I’ve been seeing frequently at work is a list of items on the frontend that is filterable, sortable, and searchable.&lt;/p&gt;
&lt;style&gt;astro-island,astro-slot,astro-static-slot{display:contents}&lt;/style&gt;&lt;script&gt;(()=&gt;{var e=async t=&gt;{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event(&quot;astro:only&quot;));})();&lt;/script&gt;&lt;script&gt;(()=&gt;{var A=Object.defineProperty;var g=(i,o,a)=&gt;o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=&gt;g(i,typeof o!=&quot;symbol&quot;?o+&quot;&quot;:o,a);{let i={0:t=&gt;m(t),1:t=&gt;a(t),2:t=&gt;new RegExp(t),3:t=&gt;new Date(t),4:t=&gt;new Map(a(t)),5:t=&gt;new Set(a(t)),6:t=&gt;BigInt(t),7:t=&gt;new URL(t),8:t=&gt;new Uint8Array(t),9:t=&gt;new Uint16Array(t),10:t=&gt;new Uint32Array(t),11:t=&gt;1/0*t},o=t=&gt;{let[l,e]=t;return l in i?i[l](e):void 0},a=t=&gt;t.map(o),m=t=&gt;typeof t!=&quot;object&quot;||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=&gt;[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,&quot;Component&quot;);d(this,&quot;hydrator&quot;);d(this,&quot;hydrate&quot;,async()=&gt;{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest(&quot;astro-island[ssr]&quot;);if(e){e.addEventListener(&quot;astro:hydrate&quot;,this.hydrate,{once:!0});return}let c=this.querySelectorAll(&quot;astro-slot&quot;),n={},h=this.querySelectorAll(&quot;template[data-astro-template]&quot;);for(let r of h){let s=r.closest(this.tagName);s!=null&amp;&amp;s.isSameNode(this)&amp;&amp;(n[r.getAttribute(&quot;data-astro-template&quot;)||&quot;default&quot;]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&amp;&amp;s.isSameNode(this)&amp;&amp;(n[r.getAttribute(&quot;name&quot;)||&quot;default&quot;]=r.innerHTML)}let p;try{p=this.hasAttribute(&quot;props&quot;)?m(JSON.parse(this.getAttribute(&quot;props&quot;))):{}}catch(r){let s=this.getAttribute(&quot;component-url&quot;)||&quot;&lt;unknown&gt;&quot;,v=this.getAttribute(&quot;component-export&quot;);throw v&amp;&amp;(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute(&quot;props&quot;),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute(&quot;client&quot;)}),this.removeAttribute(&quot;ssr&quot;),this.dispatchEvent(new CustomEvent(&quot;astro:hydrate&quot;))});d(this,&quot;unmount&quot;,()=&gt;{this.isConnected||this.dispatchEvent(new CustomEvent(&quot;astro:unmount&quot;))})}disconnectedCallback(){document.removeEventListener(&quot;astro:after-swap&quot;,this.unmount),document.addEventListener(&quot;astro:after-swap&quot;,this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute(&quot;await-children&quot;)||document.readyState===&quot;interactive&quot;||document.readyState===&quot;complete&quot;)this.childrenConnectedCallback();else{let e=()=&gt;{document.removeEventListener(&quot;DOMContentLoaded&quot;,e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=&gt;{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&amp;&amp;this.lastChild.nodeValue===&quot;astro:end&quot;&amp;&amp;(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener(&quot;DOMContentLoaded&quot;,e)}}async childrenConnectedCallback(){let e=this.getAttribute(&quot;before-hydration-url&quot;);e&amp;&amp;await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute(&quot;opts&quot;)),c=this.getAttribute(&quot;client&quot;);if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=&gt;this.start(),{once:!0});return}try{await Astro[c](async()=&gt;{let n=this.getAttribute(&quot;renderer-url&quot;),[h,{default:p}]=await Promise.all([import(this.getAttribute(&quot;component-url&quot;)),n?import(n):()=&gt;()=&gt;{}]),u=this.getAttribute(&quot;component-export&quot;)||&quot;default&quot;;if(!u.includes(&quot;.&quot;))this.Component=h[u];else{this.Component=h;for(let f of u.split(&quot;.&quot;))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute(&quot;component-url&quot;)}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,&quot;observedAttributes&quot;,[&quot;props&quot;]),customElements.get(&quot;astro-island&quot;)||customElements.define(&quot;astro-island&quot;,y)}})();&lt;/script&gt;&lt;astro-island uid=&quot;Fcwm5&quot; component-url=&quot;/home/runner/work/website/website/src/react/SortFilterSearchList.tsx&quot; ssr client=&quot;only&quot; before-hydration-url=&quot;astro:scripts/before-hydration.js&quot; opts=&quot;{&amp;quot;name&amp;quot;:&amp;quot;SortFilterSearchList&amp;quot;,&amp;quot;value&amp;quot;:&amp;quot;react&amp;quot;}&quot;&gt;&lt;/astro-island&gt;
&lt;p&gt;There should be an optimal order for these three operations.&lt;/p&gt;
&lt;p&gt;Testing a few combinations:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1_000_000 Items&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. Filter: &amp;#39;red&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. Sort by: &amp;#39;A-Z&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. Search: &amp;#39;a&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Time: 86ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. Sort by: &amp;#39;A-Z&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. Search: &amp;#39;a&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. Filter: &amp;#39;red&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Time: 220ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. Sort by: &amp;#39;A-Z&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. Filter: &amp;#39;red&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. Search: &amp;#39;a&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Time: 229ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. Filter: &amp;#39;red&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. Search: &amp;#39;a&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. Sort by: &amp;#39;A-Z&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Time: 61ms&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These results aren’t too surprising.
Sorting first provides the longest time at around 225ms while sorting last is fastest at around 60ms.
Quick Sort is O(n log n) which is much longer than the O(n) filtering on a single color.
Searching a string is naively an O(m * n) operation but can also be as fast as O(n).&lt;/p&gt;
&lt;p&gt;Taking these into account, we want to reduce the size of the list by as much as possible
before sorting.
We could even combine filtering and searching in the same loop.&lt;/p&gt;
&lt;p&gt;So the order of operations is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Filter - always O(n) since the number of filter options is generally pre-defined&lt;/li&gt;
&lt;li&gt;Search - as fast as O(n)&lt;/li&gt;
&lt;li&gt;Sort - O(n log n) pretty slow considering the other contenders.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Probably the most interesting part of these results is that if you mess up the
order and sort first, the entire operation would take almost 4x as long as if you sorted last.&lt;/p&gt;</description><pubDate>Thu, 27 Mar 2025 00:00:00 GMT</pubDate></item><item><title>Five Months of Coffee</title><link>https://enochchau.com/blog/2024/five-months-of-coffee/</link><guid isPermaLink="true">https://enochchau.com/blog/2024/five-months-of-coffee/</guid><description>&lt;p&gt;I quit drinking coffee between June and October of last year.
I wanted to prove to myself I could quit my caffeine addiction and I did, but now I’m back.
The taste of coffee, the joy of trying different flavors, the dreamy energy infusion, it’s too much to ignore.
This time around is a celebration of friendship, many of these bags were gifted to me by friends from around the world.&lt;/p&gt;
&lt;img src=&quot;/_astro/10_2023-to-2_2024-coffee-bags.BVXsAeNq_ZBSmgs.webp&quot; alt=&quot;coffee bags&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Roaster&lt;/th&gt;&lt;th&gt;Tasting Notes&lt;/th&gt;&lt;th&gt;Roast Date&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Gitesi, Rwanda&lt;/td&gt;&lt;td&gt;Four Barrel, &lt;em&gt;San Francisco, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;White Flower, Apricot Jam, Black Tea, and Jasmine&lt;/td&gt;&lt;td&gt;October 10, 2023&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Shore Leave Hook, Brazil and Colombia Blend&lt;/td&gt;&lt;td&gt;Andytown, &lt;em&gt;San Francisco, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Dark Chocolate, Sweet Tobacco, Honey-Roasted Peanuts&lt;/td&gt;&lt;td&gt;November 13, 2023&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rita Lopez, Colombia&lt;/td&gt;&lt;td&gt;Sey, &lt;em&gt;Brooklyn, New York&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Plum, Honeydew, Delicate Malic Acidity&lt;/td&gt;&lt;td&gt;December, 18, 2023&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Yaye Chericho, Ethiopia&lt;/td&gt;&lt;td&gt;Sey, &lt;em&gt;Brooklyn, New York&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Ripe Tropical Fruit, Lively Acidities, Botanicals&lt;/td&gt;&lt;td&gt;December, 18, 2023&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hangadhi BIO, Ethiopia&lt;/td&gt;&lt;td&gt;Bel Horizon, &lt;em&gt;Paris, France&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Strawberry, Grapefruit, Hazelnut, Chocolate&lt;/td&gt;&lt;td&gt;January 7, 2024&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;Steven, &lt;em&gt;Taipei, Taiwan&lt;/em&gt;&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Of the gifted bags, the first two are from Sey in Brooklyn.
Max told me about Sey when we were in New York, “It’s one of my favorite coffee shops” he said.
Mabel and I were going to visit Sey last time &lt;a href=&quot;/blog/2023/hello-new-york/&quot;&gt;I was in New York&lt;/a&gt;
but my trip ended too soon and we never made it out there.
Then I got a surprise in the mail ten months later.
For Christmas, she had sent me two bags of coffee, a bittersweet, acidic, and caffeinating reminder of our time
together.&lt;/p&gt;
&lt;p&gt;Between New York and now, I also took a trip to Europe with Max where we spent some
&lt;a href=&quot;https://enochchau.com/blog/2023/paris-nights-part-1/&quot;&gt;nights&lt;/a&gt; in &lt;a href=&quot;https://enochchau.com/blog/2023/paris-nights-part-2/&quot;&gt;Paris&lt;/a&gt;,
&lt;a href=&quot;https://enochchau.com/blog/2023/berlin-night/&quot;&gt;Berlin&lt;/a&gt;, and London.
He went back to Paris later in November and upon his return, he said he had something to give me.
&lt;a href=&quot;https://tiramisu.bearblog.dev/coffee-gift/&quot;&gt;Coffee is an easy gift&lt;/a&gt;, I shouldn’t have been surprised,
nevertheless, to my surprise, he took me to that coffee university with the bag of beans from Bel Horizon in Paris.
We brewed it together that day in my apartment and did a little coffee tasting session with
the other beans I had at home.&lt;/p&gt;
&lt;p&gt;A few weeks later, We did another coffee tasting session with Max’s friend Steven.
Steven is from Taipei where he was attempting to start his own roastery and coffee shop.
That didn’t work out quite well and later he moved to the States, but he brought with him some of the coffee he had roasted.
We had about eight bags and it reminded me of the &lt;a href=&quot;http://localhost:4321/blog/2023/four-barrel-cupping&quot;&gt;cupping session at Four Barrel&lt;/a&gt;
with all the different bags lined up in a row, each accompanied by a cup of coffee,
split between three friends.
I can’t remember the origins or tasting notes we came up with but it was delicious nonetheless.
The clear green coffee bag was Steven’s own design and we chatted about his process and the saturated market of Taipei’s coffee scene.
The city is filled to the brim with coffee shops of all sorts with beans from all around the world.
Every year, all these baristas and roasters come together to compete and share techniques in a huge coffee convention at the Nangang Exhibition Center.
He showed us photos of lines of roasters and their machines, demoing their techniques to live audiences.
In Taiwan, most roasting machines have an array of sensors to track temperature and humidity, all graphed on an electronic display.
It’s down to more of a science rather than the older method of smelling and feeling the coffee that the old masters still practice.
But that’s just the slow creep of technology and I think that’s fine.
It’s a different kind of artistry and we cannot discount it just because of it’s scientific influences.&lt;/p&gt;</description><pubDate>Tue, 27 Feb 2024 00:00:00 GMT</pubDate></item><item><title>Washcloth</title><link>https://enochchau.com/blog/2024/washcloth/</link><guid isPermaLink="true">https://enochchau.com/blog/2024/washcloth/</guid><description>&lt;p&gt;The washcloth was hanging on the hook by the shower.
I had received it in the mail earlier in the day and hung it up dutifully for later use.
Yesterday, I had already noticed that the bar of soap was already not much of a bar.
Today, it was looking much more like a mush of mashed potatoes.
The water was running and hot so I stepped in.
I wet the washcloth as the packaging instructed, macerating the mush to generate a rich lather.
It felt like a waste of soap, that bit of mush was probably enough for today and tomorrow,
but I wasn’t to want, the Kirkland size package was sitting beneath the sink, enough for more than a year of use.
I had tagged along with my flatmate at the beginning of our lease to Costco.
As we pushed through the warehouse, we judiciously chose our household needs:
a pack of dish towels and a bail of toilet paper; an assortment of detergents, soaps, and cleaning products; oversized bags of snacks, vegetables, and frozen food.
I thought about how, as a child, I would push that same cart through that same warehouse,
differing only by geolocation and company, the company of my parents.
But the bulk products and warehouse shelves and workers handing out samples to eager families were identical
to my memory, a memory I hadn’t visited then in many years.
Once again, we were buying things together, a haphazardously thrown together flat-family of three, furnishing and supplying our home,
and finally sixteen bars of dove soap, the same brand as long ago, neatly boxed individually in paper and bound together in clear plastic, landed in the cart.
Now, it was sitting beneath the sink, three bars short but providing me the mental comfort to use two days worth of soap in one.
I touched the soapy washcloth to my arm, unsure how much pain my nylon exfoliant would cause.&lt;/p&gt;
&lt;p&gt;It hurt.&lt;/p&gt;
&lt;p&gt;Up and down my arms and chest, I scrubbed.&lt;/p&gt;
&lt;p&gt;It hurt.&lt;/p&gt;
&lt;p&gt;I wrapped the two ends around my hands like I had seen the Ojisan at the Sento do and rubbed the taunt cloth against my back.
The memory of his bare hairy ass and unruly pubic bush with his flaccid penis barely peaking out was
not the only noteworthy thing I remembered from my daily routine at the public bathhouses in Tokyo.&lt;/p&gt;
&lt;p&gt;It hurt. Both the memory and the scratchy roughness on my skin.&lt;/p&gt;
&lt;p&gt;Down and up my legs, I scrubbed, between my ass and crotch, to my feet and between my toes.&lt;/p&gt;
&lt;p&gt;It hurt.&lt;/p&gt;
&lt;p&gt;Layers of dead skin, imperceptible to the human eye, rolled off my body and into the drain.
This was a new level of cleanliness I had never experienced.
I hacked a cough and now I wish I could shove this washcloth down my throat to scrub away the muck in my lungs.
Years of joints, blunts, and bong smoke followed by cigarettes, hookah, and vaporizers lead
to a couple of occasions of month long coughs that would not subside.
I wished desperately to rid myself of my dependencies but yet everyday I would wake up and sip
vapor from a pink and blue device that no longer provided a high, only a relief
from the eight hours of abstinence from the night before.
But alas, I am only human, so I rung and hung my washcloth and rinsed clean what I could of my body.&lt;/p&gt;</description><pubDate>Tue, 06 Feb 2024 00:00:00 GMT</pubDate></item><item><title>I Hate Vegas</title><link>https://enochchau.com/blog/2024/i-hate-vegas/</link><guid isPermaLink="true">https://enochchau.com/blog/2024/i-hate-vegas/</guid><description>&lt;p&gt;Jason got us tickets to see The Chainsmokers at XS at the Wynn. It was my
second time going to The Strip as an adult and I wasn’t too keen on it.
The first time, I went to watch Mitsuru play roulette while he was charging his car.
He promptly lost $50 on red which was just enough time for his to car to finish charging.
That was my first exposure to casino gambling, it will probably be my last.
At least this time, we were going clubbing, which is something I have done
before and have had fun doing.&lt;/p&gt;
&lt;p&gt;We walked up to the bouncer at the front of the line. Jason was before me and showed
his ID. The man stared at him for a long time, his glare bouncing between the card
and his subject.&lt;/p&gt;
&lt;p&gt;“Smile for me” he said. He was fucking with us at this point. No one needs that
long to verify an ID. And he finally waved Jason in.
I was next and he asked me a few questions, but as I walked into the foyer, we
were directed back out and around to the casino floor. We walked in circles,
clearly going against the flow of the posted signs, putting all our trust
in unclear directions given by a man who scrutinized us for our childish
appearances.&lt;/p&gt;
&lt;p&gt;“Do we look like kids?” I asked Jason. Asian people tend to look younger.
I’ve often wondered about the thoughts of bartenders and bouncers and waiters and cashiers when
I get carded. It’s slowly wormed into my subconscious and made me wonder if
people think I look young, and more importantly, if they treated me differently
because of that.&lt;/p&gt;
&lt;p&gt;We found our way in, through the glowing maze of cigarette haze and slot machines just
to wait again in another line by the pool, just to get into the actual club.
I was in disbelief. When did I ever buy a ticket to something, just to not be let in?
Only in Vegas. I hate Vegas.&lt;/p&gt;
&lt;p&gt;We were moving, but slowly. The man beside me had thin long strands of hair that
parted on the left side of his forehead. He struck a cigarette and I made out
the Seven Stars package as he slipped it back into his pocket.&lt;/p&gt;
&lt;p&gt;“Are those Seven Stars?”&lt;/p&gt;
&lt;p&gt;“Uh Yeah”&lt;/p&gt;
&lt;p&gt;“Are you from Japan?”&lt;/p&gt;
&lt;p&gt;“Yeah I am”&lt;/p&gt;
&lt;p&gt;He told me his name is Kohei. He was in Vegas for CES, sent there by Honda.
We chatted for a bit, I tried using my bits of Duolingo Japanese, just
to practice; his English was far better. I bummed a cigarette and we talked
about EDM and DJs. Many years ago, he was DJ Swordfish. He doesn’t DJ anymore
but he keeps the vibe alive and goes to see his favorite DJs whenever he can.&lt;/p&gt;
&lt;p&gt;The line wasn’t moving and we were waiting in the freezing cold.
I could feel the cold seeping through my thin white dress shirt.
Moments of comfort came when we moved past the heat lamps.
Looking back, the line stretched past the heat lamps and I could see girls in
thin dresses huddling together in the freezing cold.&lt;/p&gt;
&lt;p&gt;Jason was getting impatient. He checked the front of the line and came back to report.
“There’s a big group of people at the front. A lot of people are slipping past.”
He left and came back again, “I think we should try going up”.
So we did, the three of us, Jason, Kohei, and I.
We walked right up to a huge cork of people plugging the line.
As We walked up and cut the queue, I saw even more people doing the same even
farther ahead of us. It was clear no one was getting in anytime soon, the saving
grace being that it was warmer in the throng of club goers.&lt;/p&gt;
&lt;p&gt;“What if we just jump the fence?” I asked. I was cold and getting desperate.
The idea danced in my mind.&lt;/p&gt;
&lt;p&gt;“Dude I’m down”, Jason said as he hopped up to sit on the railing.&lt;/p&gt;
&lt;p&gt;“Nah Nah Nah man…”, I pulled him back down.&lt;/p&gt;
&lt;p&gt;Kohei turned around to watch what we were doing but in that moment, I knew
we would leave him behind.&lt;/p&gt;
&lt;p&gt;We watched the bouncers for awhile. There was one positioned at the front of the
line, letting people through. He was constantly preoccupied with having to field
questions from cold party goers. Another man was standing directly in our path in,
but his back was turned to us. We could slip by him, pretending to have been
at one of the outdoor tables. Another bouncer was patrolling back and forth, but
he came and went and the next time he went, we both got up on the railing.&lt;/p&gt;
&lt;p&gt;I looked at Jason and he looked at me.&lt;/p&gt;
&lt;p&gt;“Ok go go”&lt;/p&gt;
&lt;p&gt;My heart was racing as we slipped over the railing, across the small dirt plot and into
a table. Jason pushed on ahead, practically running past a bouncer. I was close on his heels
as we walked into the room.
I was waiting for a hand to come down on my shoulder and pull me back out the door
but it never came. We walked past another bouncer and he didn’t even turn to look
at us.&lt;/p&gt;
&lt;p&gt;“Oh my god, I can’t believe we did that.”&lt;/p&gt;
&lt;p&gt;“That was like climbing a high ball dude.”&lt;/p&gt;
&lt;p&gt;I grabbed his shoulder in disbelief. We were in.
We wandered around, past a table of girls with flashing drinks, stopping quickly
at the bathroom. In typical Vegas fashion, the sink was loaded with cigarettes,
cologne, and candy, all of which I avoided. If Vietnam taught me anything, it’s
that nothing is free and you should always be expected to pay a price for taking
something from a stranger.&lt;/p&gt;
&lt;p&gt;“We gotta get to the floor” I shouted in Jason’s ear. I pushed through the crowd in front of me,
making my way towards the floor entrance, turning periodically to grab Jason
through the crowd and pull him closer.&lt;/p&gt;
&lt;p&gt;Bouncers blocked our way down.
We pressed against the “line” of people waiting to get on the floor. A group of
girls on a birthday party night tried to push past us but I wouldn’t let them through.
I paid to be here. They probably didn’t. Girls can get in to any club for free.
In fact, guys can get in a club for free too, that is, if they bring enough girls.
Vegas clubs treat girls as a commodity. Women are not the target audience, they
are the marketing material for the thirsty horny men to flaunt their wealth at.
The only benefactor of this gaudy parade being the proprietor of the establishment.
I wasn’t here for any of that, I just wanted to get to the damn floor to dance
my heart out with The Chainsmokers. And we had already gotten this far, I was so close.&lt;/p&gt;
&lt;p&gt;“Are you trying to get down there?” a bouncer tapped me on the shoulder.&lt;/p&gt;
&lt;p&gt;“Uh, yeah man.”&lt;/p&gt;
&lt;p&gt;“How many people you got?”&lt;/p&gt;
&lt;p&gt;“Two.”&lt;/p&gt;
&lt;p&gt;“Hundred bucks, I get you down there right now.”&lt;/p&gt;
&lt;p&gt;“Sorry man, I don’t have any cash.”&lt;/p&gt;
&lt;p&gt;I was tempted. If I had the cash in my pocket I might just be a hundred
dollars poorer writing this now. But we got down to the stage soon enough.
I hate Vegas. Money is everything in Vegas. It buys you women and respect and
privilege to jump the queue and friends and free hotel rooms until you gamble
it all away and get dumped on the curb, broke and alone.&lt;/p&gt;
&lt;p&gt;Despite it all, I had fun that night. I jumped and bounced to the beat, belted out
the lyrics to the 2000’s era throwback songs I grew up listening to on the radio.
I screamed at the top of my lungs “Keep the vibe alive” in a vain attempt to awaken
the garden of statues around me. And through it all I had Jason to crash against,
to hold and sway together with. We head banged and jumped to the rhythm in that
garden of statues. And occasionally we’d find another wanderer in the garden
and together we’d jump again.&lt;/p&gt;</description><pubDate>Sat, 13 Jan 2024 00:00:00 GMT</pubDate></item><item><title>Four Barrel Cupping</title><link>https://enochchau.com/blog/2023/four-barrel-cupping/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/four-barrel-cupping/</guid><description>&lt;p&gt;I was sitting at &lt;a href=&quot;https://www.fourbarrelcoffee.com/&quot;&gt;Four Barrel&lt;/a&gt;,
for the first time, finishing up Cormac McCarthy’s “No Country for Old Men”
when a man in a blue flannel walked over to tell me that they were doing coffee tastings up at the pour over bar.
“We’re doing it from &lt;a href=&quot;https://www.fourbarrelcoffee.com/products/public-coffee-tasting-8-12&quot;&gt;2-3&lt;/a&gt;,
so no rush” he told me. I took that as a sign to take my time and finish up
my book and cappuccino before sauntering over to check it out.&lt;/p&gt;
&lt;p&gt;Twelve bags of coffee were on display on the wooden counter top. Each
bag was joined by two cups of cupping style brewed coffee.
As I arrived, he was finishing up his explanation to a young Chinese couple.
“We brew them like this since it would take too long to make twelve pour overs just for a tasting.”
When he saw me, he started over again, explaining how each cup contained
“about 12 grams of ground coffee, and I just add hot water”.&lt;/p&gt;
&lt;p&gt;He pointed me towards a cup full of spoons and a shot glass.
“You get a spoonful like this”, he dipped a spoon into the brew, “and then put it in the glass
before tasting. And these”, he pointed towards cups of water interspersed between the coffee, “are
for rinsing the spoon”. I nodded along, perhaps more enthusiastically than I felt.&lt;/p&gt;
&lt;p&gt;When I got to the end of the line of coffee, another man had come around and was
tasting as well. I could tell he worked there from the way he slurped and
his tight black shirt and frayed skinny jeans, it seemed like the unofficial uniform
for these SF coffee hipsters.&lt;/p&gt;
&lt;p&gt;“Do you work here?”&lt;/p&gt;
&lt;p&gt;“Yeah, I do”&lt;/p&gt;
&lt;p&gt;“Are you also a roaster?”&lt;/p&gt;
&lt;p&gt;“No, but I work with Kevin on the coffee kegs, I also help out with education.”&lt;/p&gt;
&lt;p&gt;“Oh that’s cool, like when you say kegs do you mean like cold coffee?”&lt;/p&gt;
&lt;p&gt;“Yeah, so all the cold drinks you see on the menu, we make them. We brew the coffee and tea, hot,
and then nitro chill them.”&lt;/p&gt;
&lt;p&gt;“Oh, is that to like preserve the flavor?”&lt;/p&gt;
&lt;p&gt;“Yeah”, he said hesitantly, “So with cold brews, you get some of the flavors
from the barrel, kind of like whiskey, it’s not a bad thing, but it does change the flavor.”&lt;/p&gt;
&lt;p&gt;“I see, so like for consistency between the hot and cold coffees?”&lt;/p&gt;
&lt;p&gt;“Well, hot things and cold things taste different too. Like a cold beer tastes different
than a warm beer. Usually, coffee tastes better hot.”&lt;/p&gt;
&lt;p&gt;“You wouldn’t microwave a cup of coffee though would you?”, I said with a laugh.&lt;/p&gt;
&lt;p&gt;“Actually, if it was a really bad cup of coffee I would. Like if you had a can of
Coors Light, you’d want to drink it as cold as possible, so same thing with coffee.”&lt;/p&gt;
&lt;p&gt;“How do you guys come up with the flavor profiles?”&lt;/p&gt;
&lt;p&gt;“Uh, we do tastings together, like this”, he gestured at the bar, “We all take notes
and then come up with them.”&lt;/p&gt;
&lt;p&gt;“How do you like figure out what something tastes like?”&lt;/p&gt;
&lt;p&gt;“With a lot of practice”, he smiled, “like for example ‘apricot, sour gummy worm’”, he
pointed at one of the bags on the shelf, “it obviously won’t taste exactly like that
but we do that to give customers and idea of what the coffee might taste like. That one”,
he pointed at a label, “it’s ‘jasmine’ because has a lighter flavor.”&lt;/p&gt;
&lt;p&gt;“Oh, like acidic vs bitter?”&lt;/p&gt;
&lt;p&gt;“Yeah, but there’s ranges, like if it’s on the sweeter side, we might say like a
sweet citrus, but then there’s beans, like the Ethiopian, that are more complex
and that might be more of a stone fruit”&lt;/p&gt;
&lt;p&gt;“Or like a berry?”&lt;/p&gt;
&lt;p&gt;“Yeah exactly… What’s your name?”&lt;/p&gt;
&lt;p&gt;“Enoch”&lt;/p&gt;
&lt;p&gt;“I’m Brett”, he stuck his hand out and I shook it, “Nice to meet you”.&lt;/p&gt;
&lt;p&gt;“Yeah, you too”, I called after him as he walked away.&lt;/p&gt;
&lt;p&gt;I turned back towards the bar. “What’s your name?” I asked the barista.&lt;/p&gt;
&lt;p&gt;“I’m Kevin”, He stuck his hand out.&lt;/p&gt;
&lt;p&gt;I shook his hand as well.&lt;/p&gt;
&lt;p&gt;Kevin challenged me to find differences in taste between cups, which was hard.
I’ve never thought too much about how coffee tastes, I know what I like, but the
buck stops there. We chatted about coffee brewing methods but as other patrons trickled over,
I stepped back to let him run his explanation again.
Talking to Brett and Kevin reminded me of &lt;a href=&quot;/blog/2023/six-months-of-coffee/&quot;&gt;talking to Long and Duc&lt;/a&gt; at
&lt;a href=&quot;https://www.instagram.com/dreambeansroastery/&quot;&gt;Dream Beans&lt;/a&gt; in Hanoi.&lt;/p&gt;
&lt;p&gt;Long and Duc told me about how Dream Beans was trying to cultivate third generation
coffee in Vietnam. They had partnered with farms from around Vietnam to produce
Arabica beans, a step away from Vietnam’s traditional Robusta export.
All of which was roasted on the third floor of their narrow shop.
I’m hoping they’re able to change Vietnam’s status in coffee production into a
renowned place of cultivation on the level of Colombia or Ethiopia.&lt;/p&gt;
&lt;p&gt;I love talking to these people: people passionate about the art of crafting good coffee.
People who work directly with farmers to bring specialty roasted coffee to us.
My favorite question is “So how do you make coffee at home?” as it opens the door
to talk shop about brew methods, grinders, and pour over cones, all to make their favorite cup of coffee.&lt;/p&gt;
&lt;p&gt;Thanks y’all, it’s delicious.&lt;/p&gt;</description><pubDate>Sat, 30 Dec 2023 00:00:00 GMT</pubDate></item><item><title>Roommates</title><link>https://enochchau.com/blog/2023/roommates/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/roommates/</guid><description>&lt;p&gt;This morning I woke up, with a bladder half full, to movement in the adjacent room.
I couldn’t figure out which one was the culprit so I shuffled out of bed, towards the bathroom.
As rolled myself back into the folds of my comforter, I remembered
“Oh that’s right, they’re going to Boston today. No wonder
they’re up at 5AM.” and that made me want to give a hug.
I lay in bed uneasy, uncertain, should I leave them be? But then I remembered
how I hugged my mother before she left for Taiwan, some two weeks ago. I hugged her in case of
that slim chance of catastrophe. So I know our last memory will be one I can cherish.&lt;/p&gt;
&lt;p&gt;I heard someone walk to the kitchen, so I shuffled out of bed again and waited in the doorway.&lt;/p&gt;
&lt;p&gt;“Oh, I’m sorry, did we wake you up?” Allison asked in her polite voice.&lt;/p&gt;
&lt;p&gt;“No, No, it’s alright”, I responded sleepily. “Where’s Kodai?”&lt;/p&gt;
&lt;p&gt;She went in the room and called him. He opened the door shirtless. I wasn’t
sure if I should hug him but I went for it anyways. It wasn’t a strong hug,
I doubt he’s not used to it. Last time I asked for a hug at the San Jose Airport
he turned me down. (Mitsuru gave me a solid hug in Hueco Tanks though.)
I gave Allison a hug too then shuffled back into my room.&lt;/p&gt;
&lt;p&gt;It felt a bit forced to give Kodai a hug. He’s probably not a huger.
Most of my guy friends aren’t.
But then again, I was going to miss him and I want that last memory to be a nice
one (if his plane happens to fall out of the sky or something statistically more likely).&lt;/p&gt;
&lt;p&gt;Roommates live in a gray area, a limbo, between family and friends. Unlike family,
we get to choose our roommates, but unlike friends, we live together like a family.
I’ve had roommates a few times in my life.&lt;/p&gt;
&lt;p&gt;In college I lived with five guys in a two bedroom apartment.
We didn’t share much because we were poor and no one wanted to calculate the cost
of sharing (even though it was likely cheaper). We tolerated each other, but
early morning classes and a non-existent chore schedule built up internal resentment
that would occasionally bubble over in passive aggressiveness or shouting “SHUT UP”
at 1AM when I couldn’t sleep. We didn’t have a living room or a dining table,
that space was used for desks. Without a common area, we had no where to hang out
together, and so we didn’t. Instead we drifted apart. It wasn’t a home because
we didn’t have the time or money to build it as a home and so we never became a family.&lt;/p&gt;
&lt;p&gt;Now, I have my pretend family and a good system going on at home.
We share pots and pans, cups and bowls, a record player and a Nintendo Switch, along with a communal bong and a 50lb bag of rice.
We do chores together, have meals together (at our dining table), watch TV together (in our living room), go grocery shopping together.
I still have to deal with their little annoyances, like how I sometimes re-wash
dishes so they’re clean to my standard, and they have to deal with my
annoyances too. But we tolerate each other, like a family, we know we have to
live together, at least for this year, and a loving environment beats the ugly
shouting arena I grew up in.
Sometimes it feels like this could last forever, but our family has a legally binding expiration date.&lt;/p&gt;
&lt;p&gt;So for now, I love you.&lt;/p&gt;</description><pubDate>Thu, 28 Dec 2023 00:00:00 GMT</pubDate></item><item><title>Tiki Smashed: Progression</title><link>https://enochchau.com/blog/2023/tiki-smashed/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/tiki-smashed/</guid><description>&lt;p&gt;Cramped into a small corner with stools lining the alcove, we sat and waited for
our tropical rum inspired drinks. The sky boomed above us as rain and thunder
danced on the ceiling, a mockery to nature’s true power.&lt;/p&gt;
&lt;p&gt;I was stuck to Allison again, two hyperactive hot spring monkeys, amped by booze.
Not stuck stuck, just trapped against the wall in our little corner of the tropics.&lt;/p&gt;
&lt;p&gt;“So how do you find meaning in your life?”, I asked her.&lt;/p&gt;
&lt;p&gt;“That’s what I’m trying to figure out right now. I feel like I’ve grown
roots here now. I have a stable job, a relationship and friends. But like,
my brain still isn’t comfortable.”&lt;/p&gt;
&lt;p&gt;My mind wandered as it does when intoxicated.
“Lately I’ve just been trying to find the joy in the little things.”&lt;/p&gt;
&lt;p&gt;“What do you mean?”&lt;/p&gt;
&lt;p&gt;“Like there’s stuff I stress about, mostly to find a partner right now and sometimes
it leaks, like I project. Before when I’d see a couple on the street I might
get jealous but now I try to think, ‘Oh how lovely, it’s young love’. Or seeing a
child in a stroller and thinking about how exciting their future is rather than
the bleakness of the world they might grow up in.”&lt;/p&gt;
&lt;p&gt;“That’s a good way to think about things. I still get anxiety sometimes even
though my life is stable. Like I’m always wondering what’s next, I feel like I
have to be working towards something. That’s why I picked up power lifting,
because the goals are more concrete.” She reminded me of something I had
&lt;a href=&quot;http://plumshell.com/2023/12/23/having-a-game-im-really-into-makes-every-day-incredibly-enjoyable/&quot;&gt;read&lt;/a&gt;
recently.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Apparently, when humans have free time, they tend to stress over the past and
future instead of focusing on the present.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;p&gt;Thunder crashed again as drums started. A volcano was erupting, in the literal sense.
A waiter was bringing around a bowl of molten alcohol and ‘Happy Birthday’
coursed through the room. I sang (slurred) along to the song and watched with bleary eyes.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;I turned to Andy in that dark and stormy room.&lt;/p&gt;
&lt;p&gt;“Dude, you just have to go talk to someone.”
All night I was trying to get him to fulfill his mission.&lt;/p&gt;
&lt;p&gt;“Naaaaah”&lt;/p&gt;
&lt;p&gt;“Alright, let’s make a bet. You have to talk to at least one stranger tonight.”&lt;/p&gt;
&lt;p&gt;“Eh… Naaaah”&lt;/p&gt;
&lt;p&gt;“Dude why are we even out here then? Honestly, you gotta start somewhere. Like go talk to her!”&lt;/p&gt;
&lt;p&gt;“Hmmmm nah, she’s not it.”&lt;/p&gt;
&lt;p&gt;“Oh my god”&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;I shimmied my way out of our alcove towards the bathroom.
As I walked back towards our corner, I decided I had to show him up.&lt;/p&gt;
&lt;p&gt;“So whose birthday is it?”&lt;/p&gt;
&lt;p&gt;“It’s Sophie’s!!!”&lt;/p&gt;
&lt;p&gt;“Oh my god!! Happy birthday Sophie”&lt;/p&gt;
&lt;p&gt;But that little comment just reminded me of all the messages I’d sent to strangers online.
All those little comments and quips that amount to sweet nothings.
But it’s a starting point. A point to see the world as a brighter place.
Just to talk to people, to hear their loves and woes.
I’m the main character in my own story but so are they. And their lives are just
as rich as mine, filled with hopes and dreams, wants and desires.
And to crack open just a little bit of that… that might just be the meaning of life.&lt;/p&gt;</description><pubDate>Mon, 25 Dec 2023 00:00:00 GMT</pubDate></item><item><title>Berlin Night</title><link>https://enochchau.com/blog/2023/berlin-night/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/berlin-night/</guid><description>&lt;p&gt;&lt;em&gt;a story from a night in Berlin in September 2023&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Max had made a new friend in Berlin and she invited him to China - City, the best place for Chinese food in Berlin according to her mother.
I was invited too but I wanted to climb that evening so I ditched dinner, but
I joined them later after dinner.
He had the keys to our apartment so we had to meet up eventually.&lt;/p&gt;
&lt;p&gt;I could have taken the bus but I decided to walk a half hour.
Walking is a great way to discover what a neighborhood has to offer, every shop or restaurant I pass that’s half interesting, I’ll mark down on a map for later.
I planned on getting döner for dinner but was stopped by this wacky clip art looking crown wearing peanut on the sign for Basmah, a Sudanese restaurant.
I took my time on the sidewalk, looking up reviews and translating the menu before walking in.
The guy behind the counter took my order and like a time traveling yo-yo, I sat down just to be summoned back to the counter for my food.
He asked me something in German and I blindly said “ja” so he proceeded to drench my meal in peanut sauce.
Pretty good, just don’t forget to add hot sauce.&lt;/p&gt;
&lt;p&gt;After my dinner, I kept walking, passing closed shops, bustling bars, packed restaurants, and a couple of kids filming a music video under an overpass.
Arriving, I walked into the Chinese restaurant to a sight that felt like just like the banquet hall style Dim Sum restaurants in Hong Kong.
I’ve seen that same style of restaurant in many parts of the world that Southern Chinese people have immigrated to.
These restaurants remind me of my childhood since I went to my fair share of them as a kid.
Most of the people inside were also Chinese, it’s always a good sign when Chinese people are eating the Chinese food.&lt;/p&gt;
&lt;p&gt;As quickly as I walked in, I was pulled back outside.
The three us decided to go on a journey to find a place to drink.
We walked to a bar that we couldn’t find.
We walked past a bar that looked nice but was full of older faces.
We walked by a bar that sounded much too loud for a conversation.
Finally, we landed on a quaint cocktail bar with low lights and comfortable interior called Windhorst.
The entire place was run by a single middle aged man wearing circle glasses and an ocean blue shirt.
He played host, waiter, and bartender to the small crowd, coming to tables to take orders,
mixing up drinks, and bringing them back.
In his downtime he cleaned, restocked the bar, and refilled carafes of water.
I was fascinated watching his efficiency and reflexive competence in keeping us (the guests) well taken care of.
We talked to him in a mix of German and English.
I overheard a mix of German and English coming from the other tables.
At one point, he locked the door because he couldn’t deal with more customers.&lt;/p&gt;
&lt;p&gt;We sat to sip and talk.
Her name was Janina and she was German Born Chinese (GBC), well not exactly, but more or less.
Funny enough, we spoke mostly in Chinese.
She said her Chinese was better than her English so that’s the way we went.
Max introduced me to a number of Chinese/Taiwanese people during our trip but Janina was the easiest to talk to.
Her Chinese was easy to follow.
She didn’t use an overly complex vocabulary so I could comprehend and contribute to the conversation without too much cognitive overhead.
I could always dip back into English when I needed to and
She’d sometimes react in German which was amusing.
Our conversation was two concurrent streams of cultural similarities and cultural differences intertwined.
It’s an easy topic for people from different parts of the world to talk about.
Despite growing up in different corners of the world, GBC, ABC (American Born Chinese), it’s pretty much the same shit different country.
You played piano or violin, you speak Chinese but not really, your parents used corporal punishment,
and your parents probably pushed you to study something in school with good career prospects.
Chinese immigrants feel the same everywhere, but their children have dreams of their own.&lt;/p&gt;
&lt;p&gt;Janina told us about &lt;a href=&quot;https://www.hoepfner.de/produkte/grape/&quot;&gt;“Grape”&lt;/a&gt;, a malt grapefruit drink
she used to drink in University, so I went to find it the next day.
She kept giving me drinks to try so now you can watch the saga on Instagram.&lt;/p&gt;
&lt;h3 id=&quot;grapefruit-drink-35&quot;&gt;Grapefruit Drink ⭐⭐⭐(3/5)&lt;/h3&gt;
&lt;p&gt;I’m not a huge fan of flavored beer drinks but it’s sweet and tastes like grapefruit.&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;true&quot; data-instgrm-permalink=&quot;https://www.instagram.com/reel/CwqS9Z5Ox62/?utm_source=ig_embed&amp;utm_campaign=loading&quot; data-instgrm-version=&quot;14&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/reel/CwqS9Z5Ox62/?utm_source=ig_embed&amp;utm_campaign=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt;View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; grow: 1; justify-content: center; margin-bottom: 24px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; margin-bottom: 6px; width: 224px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; width: 144px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/reel/CwqS9Z5Ox62/?utm_source=ig_embed&amp;utm_campaign=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;&quot; target=&quot;_blank&quot;&gt;A post shared by Enoch (@uhhuhhuny)&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;
&lt;h3 id=&quot;fritz-kola-25&quot;&gt;Fritz Kola ⭐⭐(2/5)&lt;/h3&gt;
&lt;p&gt;Tastes like Pepsi. Sorry I’m more of a Coke guy.
But I read it has a lot more caffeine so maybe it’s good for a long night out.&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;true&quot; data-instgrm-permalink=&quot;https://www.instagram.com/reel/Cws9i_tIBfa/?utm_source=ig_embed&amp;utm_campaign=loading&quot; data-instgrm-version=&quot;14&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/reel/Cws9i_tIBfa/?utm_source=ig_embed&amp;utm_campaign=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt;View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; grow: 1; justify-content: center; margin-bottom: 24px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; margin-bottom: 6px; width: 224px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; width: 144px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/reel/Cws9i_tIBfa/?utm_source=ig_embed&amp;utm_campaign=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;&quot; target=&quot;_blank&quot;&gt;A post shared by Enoch (@uhhuhhuny)&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;
&lt;h3 id=&quot;club-mate-55&quot;&gt;Club Mate ⭐⭐⭐⭐⭐5/5&lt;/h3&gt;
&lt;p&gt;Kinda like &lt;a href=&quot;https://guayaki.com/&quot;&gt;Guayaki Yerba Mate&lt;/a&gt; that we have in the States.
I thought it was read “Club Mate” but it’s probably “Club Ma-Te”.
Tastes kinda like watered downed whiskey mixed with apple cider vinegar but
much sweeter.
I like drinks that are acquired tastes like whiskey and coffee and maybe Club Mate could fit in this category.&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;true&quot; data-instgrm-permalink=&quot;https://www.instagram.com/reel/Cwvaib0Ie7C/?utm_source=ig_embed&amp;utm_campaign=loading&quot; data-instgrm-version=&quot;14&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/reel/Cwvaib0Ie7C/?utm_source=ig_embed&amp;utm_campaign=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt;View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; grow: 1; justify-content: center; margin-bottom: 24px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; margin-bottom: 6px; width: 224px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; grow: 0; height: 14px; width: 144px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/reel/Cwvaib0Ie7C/?utm_source=ig_embed&amp;utm_campaign=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;&quot; target=&quot;_blank&quot;&gt;A post shared by Enoch (@uhhuhhuny)&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;</description><pubDate>Mon, 25 Sep 2023 00:00:00 GMT</pubDate></item><item><title>Paris Nights (part 2)</title><link>https://enochchau.com/blog/2023/paris-nights-part-2/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/paris-nights-part-2/</guid><description>&lt;p&gt;&lt;em&gt;a story from a night in Paris in August 2023, &lt;a href=&quot;/blog/2023/paris-nights-part-1&quot;&gt;read part 1&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The next weekend, Dayana invited us out again.
This time to an Asian fusion place called Double Dragon.
We had gone up to Sacré-Cœur earlier in the day but I went home early to work.
Max had stayed out to explore Paris and we agreed to meet up later in the night.
I smoked a cigarette at a park while waiting for him.
Parks in Paris are lush and green, which manicured lawns and neatly planted trees, sandy gravel paths and spacious park benches.
I always seemed to find some French people sitting on the grass.&lt;/p&gt;
&lt;p&gt;We met Yam for the first time, and tried to guess what kinda Asian she was.
Korean? No. Chinese? No. Japanese? No. To be fair she looks East Asian.
She’s from Thailand with ethnic Chinese parents originally from Vietnam.
Seems like everywhere I go I hear similar stories to my fathers, Chinese people leaving Vietnam.
Same shit different country.
My dad just drew a different straw and ended up in the US instead of Thailand.
Yam doesn’t speak Chinese though.
I desperately hold onto my ability to speak Chinese.
I know I’m American but I want to stay close to my roots, perhaps because I believe
it makes me special, maybe &lt;a href=&quot;https://open.spotify.com/track/2DZdof9VHreWvnCBAuhNr2?si=903d8b969f1c4241&quot;&gt;I’m just old fashioned&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We had less to drink that night, 1 bottle of wine between four people.
The food was good, a little bit more memorable.
We finished dinner with beignets and now my three fellow diners believe I’m obsessed with donuts.&lt;/p&gt;
&lt;p&gt;Then we went to a Jazz Bar.
I found a low-key one called Sunset/Sunside I wanted to check out but we ended up going to Yam’s suggestion: Le Caveau de la Huchette.
Max asked Elizabeth to come so we waited for her outside.
Max had met Elizabeth online a few weeks before we landed in Paris.
They had gone out a few times together already.
There was a door price, but not too expensive.
We walked into the bar, then down to the basement, a dark room with loud music, packed with people dancing to the lively beat of the band.
It’s a different vibe than the sit and listen vibe from the jazz clubs I’m used to. But the energy was amazing.&lt;/p&gt;
&lt;p&gt;We got drinks and sat above the dance floor, waiting for the next set to start.
Old French men danced with young British girls to the interlude music blaring over the speakers.
I wanted to get down.
Dayana kept pointing at the boys, “See they’re all looking for girls to dance with, go find a French girl”.
I’m shy, not drunk enough yet.
So I asked Yam and Dayana if either of them would go down with me.
Yam was a bit bashful but relented after the first song.
The band boomed with swinging jazz beats and complex solos, the MC was American, he controlled the crowd with a booming friendly voice I could only imagine was from New Orleans.
New Orleans because they played “Oh When the Saints”.
We danced in that sweaty crowded basement full of young faces and British accents.
Afterwards, I kept joking with Max that it was the first time I had felt the touch of a woman in 6 months.&lt;/p&gt;
&lt;p&gt;Max wanted to drink more but Yam and Dayana wanted to catch the train home.
We parted ways, Elizabeth, Max and I continuing on across the Seine to find somewhere else to drink.
We talked for awhile, about our respective countries, difference in how we grew up, similarities, places we’d been to.
At some point, Max got up to use the bathroom and I asked Elizabeth point blank “Why did you decide to meet Max?”.&lt;/p&gt;
&lt;p&gt;She said she was just going with the flow but “in France, usually people date for a girlfriend and not just to be friends”.&lt;/p&gt;
&lt;p&gt;“I mean, we do that in the States too, but it just depends on the person”.&lt;/p&gt;
&lt;p&gt;It made me wonder if Elizabeth had different expectations from Max on whatever this fling was.
I told her about meeting a girl in New York without much conclusion.
I always wonder if meeting people while traveling is worth it.
With the internet, we can still stay in touch but it’s impossible to replace a conversation in person.
I told my New York landlady about the girl I met and she said that she had met men while traveling too, sometimes they keep in touch, sometimes it fizzles out.
But she was probably pushing 40, still traveling the world, single, with a teenage kid in College so I can’t say for sure how much she knows about commitment.&lt;/p&gt;
&lt;p&gt;Maybe we are just looking for friends.
I know Max is only looking for friends when he’s traveling.
That’s why I keep putting “date” in quotations, because on the surface, they seem like “dates” but really, he just wants someone else to hang out with besides me, which is fair.
Imagine spending every hour of an entire month with the same person, you’d probably want to get the hell away from them eventually.
We got into our fair share of fights but we finished that trip together and perhaps that’s a testament to our friendship.&lt;/p&gt;</description><pubDate>Tue, 19 Sep 2023 00:00:00 GMT</pubDate></item><item><title>Paris Nights (part 1)</title><link>https://enochchau.com/blog/2023/paris-nights-part-1/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/paris-nights-part-1/</guid><description>&lt;p&gt;&lt;em&gt;a story from a night in Paris in August 2023&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Max said his friend had invited us out for dinner on Friday night.
We were late, I can’t remember why now, maybe Max had come back late from another “date”.
Either way, we went up to 11th Arrondissement, about a half hour late, to a wine bar restaurant type thing.
I can’t remember the food much now, I’m sure there’s photos somewhere but the things I do remember was having the hardest time figuring out what to get.
Menus were in French, which isn’t a problem with our modern digital translation technology, but we’re an indecisive bunch.
Everyone was a little afraid to pick something bad, I put in a few suggestions, Max put in a few, Dayana ended up choosing the finals because, well, she knows French, because, well, she’s French.
We started each with a glass of wine, which quickly progressed to a bottle.
The food was good, but I enjoyed the company and alcohol much more.&lt;/p&gt;
&lt;p&gt;After dinner, we slid out of the restaurant to find more drinks.
Max found one, up the street, a cocktail place, his kinda vibe.
So we rolled through, had another drink.
The whole walk over, I was trying to finish duolingo.
It was already 11:30pm and I wasn’t going to lose my streak.
I wonder if they thought I was crazy, for sticking my phone up to my ear every few seconds while trying to keep a conversation going.&lt;/p&gt;
&lt;p&gt;We talked over drinks more.
About differences between our home countries, our experiences in Taiwan (we’d all lived in Taiwan before), and talking to strangers.
I told my stories about talking to randoms at bars in Taipei and Tokyo so Dayana dared me to talk to someone that night.
I was at least 4 drinks in, which is a lot for me, and there was an Asian guy with a blond girl at the next table so I started yelling at them “Excusez-moi!, Excusez-moi!, parlez-vous Anglais?” except “Anglais” came out more like “Ingles”.
I only know a little Spanish, no French skills to speak of.
I kept tripping up, like when I went to buy French cigarettes.
The brand is called Gitanes so I kept saying “ji-tan-es” and the old Chinese guy behind the counter couldn’t understand me for shit, he kept saying “Marlboro?” until I pulled up the image on my phone and he was like “Ooooo Gitanes”.&lt;/p&gt;
&lt;p&gt;Anyways, the Asian guy ended up being a FBJ - French born Japanese and the girl was from Sweden.
They were a couple, and lived in London.
They were just in Paris for the weekend and were at the bar with the guy’s coworkers.
We talked about basic unmemorable things, like “where do you work?” and “where are you from?” and “how did you meet?”, nothing of much substance but that’s just what happens when you’re trying to have a group conversation with strangers that just aren’t as drunk as you are.
Well, for that matter, Max and Dayana weren’t as drunk as I was either.&lt;/p&gt;
&lt;p&gt;After asking for more bar reccs, of which we got none, we set off down the street to find another place to drink.
On our search, I saw a sign I actually recognized: “Le Chat Noir”, “The Black Cat”.
Just what I retained from my four days of French duolingo before giving up because the pronunciation was too hard.
That place ended up being kinda weird so I took us to a divey looking place down the street.
I like dive bars, Max and Dayana didn’t seem to like the vibe that much, but we just needed a place to have cheap drinks.
We didn’t stay long but Dayana talked to a guy about his dog (which was under the table), proving that, indeed, she could talk to strangers at a bar.&lt;/p&gt;
&lt;p&gt;I wanted to keep drinking, because I love drinking when I’m drunk.
So we walked a little more down.
There’s a ton of bars in 11th but a lot of them were closed.
France likes to shut down in August because everyone goes on vacation for a month.
Finally, we found a spot for our last drink.
Wrapping up, I needed to hydrate so I staggered my way over to the bar.
I asked for “trois eau” and upon hearing my barely decipherable accent, a Frenchman asked me where I was from.&lt;/p&gt;
&lt;p&gt;“California, San Francisco” I said.&lt;/p&gt;
&lt;p&gt;“Ahhhh welcome to Paris, you know in Paris we don’t drink water, we drink beer” before he took a long gulp out of his cup.&lt;/p&gt;
&lt;p&gt;I laughed, he seemed drunker than me.
I’ve heard French people aren’t too friendly with strangers, but the contrary is, everyone is friendly if they’re drunk enough.&lt;/p&gt;</description><pubDate>Sun, 17 Sep 2023 00:00:00 GMT</pubDate></item><item><title>Shibuya Night</title><link>https://enochchau.com/blog/2023/shibuya-night/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/shibuya-night/</guid><description>&lt;p&gt;&lt;em&gt;a story from when I was in Tokyo back in May of 2023&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Mits was meeting up with Sora and his girlfriend so I was alone for that night.
I figured I’d go out and do something interesting so I went down to Yoyogi park and walked around a bit.
Around dinner time, I made my way back down to Harajuku.
Someone, probably Mits, had recommended me Afuri ramen.
While eating, I watched these girls and their family. The girls were all dolled up with lots of makeup.
Their eyes also looked really big for Asian girls and I couldn’t help but think that they might have plastic under their skin.
But I remember them now because they were speaking Chinese with Taiwanese accents, but spoke to the servers in Japanese.&lt;/p&gt;
&lt;p&gt;It was still early so I wandered down towards Shibuya.
I stopped by in Tower Records and Nitori.
I like Ikea so Nitori was great, well, everything great minus the Swedish meatballs.&lt;/p&gt;
&lt;p&gt;A few days before, I had looked for foreigner bars to go to, one of which was Bar Tasuichi so I headed down in that direction.
Already, there were loud drunk kids yelling in the street.
It’s a standing bar, and I stood at the bar, smoking a pack of Camel berry menthol cigarettes from Vietnam.
An African guy came up behind me and asked me for a cigarette.
I asked him where he was from and he told me but I can’t remember now.
He said his brother had got him a construction job in Japan which he had been working for the last few years.
I asked him if he knew Japanese.
“Just speaking, no reading and writing, I don’t have time for that, man” he said.
He was wearing an Apple Watch so I figured he was doing pretty good.
After awhile, he told me he had to leave, so I turned to the other guy next to me also smoking.
He looked just like a Japanese Salary man.
I told him I was from America, he said his English was bad.
We talked a bit using Google Translate about Vietnam, baseball, and Shohei Ohtani.
While we chatted, a group of Americans came over to the bar.
I heard them speaking English and didn’t think too much of it but the guy closest to me, a bald man with sharp features asked me “What are you drinking man?”.&lt;/p&gt;
&lt;p&gt;“I’m just having a beer”.&lt;/p&gt;
&lt;p&gt;“Oh yeah, that’s what I’m about to get”.&lt;/p&gt;
&lt;p&gt;His buddy turned to him and laughed, “His English is better than your Japanese”.&lt;/p&gt;
&lt;p&gt;I smiled because they didn’t know I was a foreigner in disguise. “Yeah, that’s because I’m American”.&lt;/p&gt;
&lt;p&gt;We laughed together and I learned they were American Navy sailors, in Tokyo for a few nights.
One of the servers brought over the beers.
He wouldn’t believe that I was 20 so he asked to see my ID.
He kept asking me “何歳?” and the bald sailor kept asking me “何歳?” as well.
I just looked at them blankly like, dude, I can’t speak Japanese.&lt;/p&gt;
&lt;p&gt;“You look younger than 25” the sailor laughed, “must be the tucked in pants”.&lt;/p&gt;
&lt;p&gt;I liked his vibe. He was already in his 40’s but he livened the mood in the bar.
He kept trying to get girls to dance even though he already had a Japanese girlfriend.
After a few drinks, he turned to me and said “Don’t ever let the fire die in you, man.
Me, it’s starting to go out, but I’m trying to keep it burning for as long as I can”.
I hope I’m still the life of the party when I’m 40.&lt;/p&gt;
&lt;p&gt;The salary man left and a girl with colorful makeup and her man covered in tattoos and piercings came to replace him.
She spoke a bit of English but he didn’t.
That didn’t stop bald sailor man.
They chatted in Japanese.
I could understand a bit, they were talking about tattoo guy’s piercings.
At some point, I picked out おちんちん.&lt;/p&gt;
&lt;p&gt;“What’s it called again in English?” he asked me.&lt;/p&gt;
&lt;p&gt;“Prince Albert”.&lt;/p&gt;
&lt;p&gt;Sailor man was asking his new friend if he’d ever get a dick piercing.
It’s funny watching Japanese people get put in uncomfortable situations but sailor man’s vibe was unshakable.
Doesn’t feel too uncomfortable if you’re drunk enough.&lt;/p&gt;
&lt;p&gt;Soon enough, I finished my drink.
I traded my last cigarette to tattoo man for his last cigarette.
He had been chain smoking American Spirit menthols and god damn those things were harsh but I couldn’t waste his last cigarette.&lt;/p&gt;
&lt;p&gt;As I walked down the street towards the station, I watched a guy piss into a basement doorway as his drunk friends laughed in amazement. Doesn’t feel too uncomfortable if you’re drunk enough.&lt;/p&gt;</description><pubDate>Fri, 15 Sep 2023 00:00:00 GMT</pubDate></item><item><title>Push Me in the Mosh Pit</title><link>https://enochchau.com/blog/2023/push-me-in-the-most-pit/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/push-me-in-the-most-pit/</guid><description>&lt;p&gt;“I think this is the most Chinese people we’ve seen in the last week”, Max said, looking around
at all the East Asian faces around us. It’s a dark room and we’re about to listen to some loud,
rhythmically difficult, mini-band music (Chinese Football) in a tiny indie rock Berlin venue.&lt;/p&gt;
&lt;p&gt;The guy in front me has long hair, he looks like I did back in College, except maybe
2 inches taller. He pulls his friend over next to him.
They’re both wearing backpacks.
Fuck dude, backpacks take up the space of like two people in a crowd.
But the crowd is thin so I don’t mind.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;“This is our song about uniting two halves” the bassist whispered into the mic,
“So I want to divide the room in half, make a space in the middle. Now
when I saw ‘go’ I want you all to come together”.&lt;/p&gt;
&lt;p&gt;The man was literally wall of deathing a math rock show.
And I was all for it. Music is made for us to move our bodies to.
The worst shows are those slow indie shows where everyone sways softly but
the beat never gets fast enough to really move.
I was smiling from ear to ear. My body felt electric. It was time to go.&lt;/p&gt;
&lt;p&gt;“GO”&lt;/p&gt;
&lt;p&gt;German people running. Chinese people running. Bodies colliding, arms flailing.
Get pushed out, run back in, jump up, ram someone with my shoulder while flinging
my arms out.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;I really didn’t think we would mosh at a mini-band concert.&lt;/p&gt;</description><pubDate>Wed, 06 Sep 2023 00:00:00 GMT</pubDate></item><item><title>Sorry I Don&apos;t Speak Your Language</title><link>https://enochchau.com/blog/2023/sorry-i-dont-speak/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/sorry-i-dont-speak/</guid><description>&lt;p&gt;Traveling is fun but it slowly chips away at my psyche.
I like experiencing new cultures but when I don’t speak the local language,
it gives every little thing a little bit of friction.
Like going to 711 in Japan and getting asked “袋はいりますか?”
and just being like “huh o.o” to such a simple question.&lt;/p&gt;
&lt;p&gt;It makes me wonder how my immigrant parents felt when they first came to the States.
It’s like when I travel and I can’t understand shit but instead of holiday it’s the rest of their life.
Us internet age babies are so lucky to have Google Translate:
for translating restaurant menus, the instructions on the washing machine,
the labels on the various body cleaning solutions in the bathroom, just simple everyday things.&lt;/p&gt;
&lt;p&gt;Traveling in Europe is definitely easier than traveling in Asia.
More people speak English, and they speak it well.
I’m lucky I’m a native English speaker, and even luckier that the rest of the world
happens to (mostly) speak English.
I’m also lucky that English is cobbled together from German and French, which
makes reading signs distantly familiar.
But imagine immigrating to Germany and having to learn German… and then also
having to learn English just to speak to tourists. What a pain in the ass.&lt;/p&gt;
&lt;p&gt;Language and culture are also deeply intertwined.
Not knowing language takes away from my cultural experience.
I can only observe, never able to engage.
The bums asking for change on the subway seem nice to me, because I can’t understand what they say.
Maybe someone swore at me as I passed them on the street, I would never know.
Of course, I can observe all the people walking around with beers at 2pm in Berlin and
look up what a Wegbeir is but the experience would be richer if I just asked the dude instead.&lt;/p&gt;
&lt;p&gt;I’ve been speaking more Chinese lately because I’ve been meeting 華人 while abroad.
These conversations are hard, my brain is simply too slow to process conversation at the going rate.
By the time I think of something to say, we might already be passed that topic.&lt;/p&gt;
&lt;p&gt;It’s hard to be polite, it’s hard to be friendly, it’s hard to be myself when I don’t speak your language.&lt;/p&gt;</description><pubDate>Wed, 06 Sep 2023 00:00:00 GMT</pubDate></item><item><title>A Sunny SF Sunday</title><link>https://enochchau.com/blog/2023/sunny-sf-sunday/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/sunny-sf-sunday/</guid><description>&lt;p&gt;Up Up, out of the station, up a million stairs. Rising from the gutters
of the subway, into the light, into the people.&lt;/p&gt;
&lt;img src=&quot;/_astro/walking_up_from_bart.w0Ioo3AK_BQljj.webp&quot; alt=&quot;walking up from bart&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I was up at Dolores for a bit, did a walk through.
I sat at a bench next to girl who I didn’t get a good look at but thought
it might be worth talking to her.
She seemed to be waiting for someone, but no one ever came.
I waited so long but I couldn’t talk to her so instead, I just sat and read
“On the Road” by Jack Kerouac.
All my dating advice I get from Jack Kerouac now, or more specifically, Dean Moriarty (Neal Cassidy),
the madman. But I don’t have as much rizz as Neal, so I’ll
study his escapades until I become mad myself.&lt;/p&gt;
&lt;p&gt;I want to start street photography. Pure candid.
You know, take a photo of people on the street. Don’t ask for permission, ask
for forgiveness.
I’m down to talk to people. I got stopped by two skaters when I took their photo
and so I showed it to them and he said “I look retarded”.
I assured him “Well there’s good ones and there’s bad ones” and
with a parting fist bump he said “I fuck with it”.&lt;/p&gt;
&lt;img src=&quot;/_astro/skaters.B1ego9B6_2hpBOK.webp&quot; alt=&quot;skaters&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I have the option hide behind a pair of sunglasses but I’d rather people see my eyes.
Watch my eye balls as I hunt for a good shot, steal their gaze for my lens,
and capture whatever hides beneath.
I want them to know I’m human too.&lt;/p&gt;
&lt;img src=&quot;/_astro/market_walking.DskYfkPB_Z8az7U.webp&quot; alt=&quot;security guard waits for the bus&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Street photography is like graffiti in a way.
With graffiti, the street is the canvas, it’s part of the medium along with spray cans.
With street photography, the street and the people are the subject, the medium being the camera.
Just like graffiti is disruptive to the street, street photography is disruptive to the
person I’m pointing a camera at.
But I figure, if someone pointed a camera in my face, I wouldn’t get too upset.&lt;/p&gt;
&lt;img src=&quot;/_astro/dog.DOmm0dZ1_Z1TMg2X.webp&quot; alt=&quot;dog&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Then I’m walking up Van Ness, up Nob Hill, but it doesn’t feel so steep today.
Up to the gym to meet up with my friends, to pull on plastic and destroy our skin,
and leave with achy fingers and a feeling of bliss.&lt;/p&gt;
&lt;img src=&quot;/_astro/benchmark_climbing.C3Fn9J4e_BQ7I6.webp&quot; alt=&quot;benchmark climbing&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Then it’s back down Nob Hill. I’m going to see a band tonight: Say Sue Me.
I’ve never even heard of them before but Max has brought me along for the
last few indie shows.
It’s at the Rickshaw Stop, I have fond memories here.
Inside I meet Max’s friends, indie heads through and through.
They can talk about bands for ages.
The kids here have black ‘X’s on their hands.
The last time I was here, I had black ‘X’s on my hands.
But I’m getting too old for this now. I still hear ringing in my ears
and it’s been a full day since the show.&lt;/p&gt;
&lt;img src=&quot;/_astro/microphone.K_hs2YC3_Z1JuFbn.webp&quot; alt=&quot;microphone&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Mon, 17 Jul 2023 00:00:00 GMT</pubDate></item><item><title>Six Months Of Coffee</title><link>https://enochchau.com/blog/2023/six-months-of-coffee/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/six-months-of-coffee/</guid><description>&lt;p&gt;Continuing from &lt;a href=&quot;/blog/2022/five-months-of-coffee&quot;&gt;last time&lt;/a&gt;, this is another six months of coffee.&lt;/p&gt;
&lt;img src=&quot;/_astro/coffee-bags.BQ01yKxp_Z1atyCR.webp&quot; alt=&quot;coffee bags&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;em&gt;Coffee bags in the photo are ordered by roast date from left to right, top to bottom.&lt;/em&gt;&lt;/p&gt;




































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Roaster&lt;/th&gt;&lt;th&gt;Tasting Notes&lt;/th&gt;&lt;th&gt;Roast Date&lt;/th&gt;&lt;th&gt;Origin&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Decaf Los Montanos&lt;/td&gt;&lt;td&gt;Ritual, &lt;em&gt;San Francisco, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Cherry Pie, Muscat Grape&lt;/td&gt;&lt;td&gt;November 29, 2022&lt;/td&gt;&lt;td&gt;Colombia&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Metapan&lt;/td&gt;&lt;td&gt;Verve, &lt;em&gt;Santa Cruz, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Green Apple, Shortbread, Cocoa&lt;/td&gt;&lt;td&gt;December 18, 2022&lt;/td&gt;&lt;td&gt;El Salvador&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Coto Brus&lt;/td&gt;&lt;td&gt;Artbean, &lt;em&gt;Brooklyn, New York&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Chocolate, Honey, Citrus&lt;/td&gt;&lt;td&gt;January 27, 2023&lt;/td&gt;&lt;td&gt;Cost Rica&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Bonde&lt;/td&gt;&lt;td&gt;Vesta, &lt;em&gt;Las Vegas, Nevada&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Floral, Green Tea, Kumquat&lt;/td&gt;&lt;td&gt;February 27, 2023&lt;/td&gt;&lt;td&gt;Ethiopia&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Las Flores&lt;/td&gt;&lt;td&gt;Devoción, &lt;em&gt;Brooklyn, New York&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Orange Blossom, Mandarin, Honeysuckle, Vanilla&lt;/td&gt;&lt;td&gt;March 1, 2023&lt;/td&gt;&lt;td&gt;Colombia&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Worka&lt;/td&gt;&lt;td&gt;Simple Kaffa, &lt;em&gt;Taipei, Taiwan&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Pineapple, Dried Mango, Chamomile, Black Tea&lt;/td&gt;&lt;td&gt;&lt;strong&gt;March 13&lt;/strong&gt;, 2023&lt;/td&gt;&lt;td&gt;Ethiopia&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sweet Dream&lt;/td&gt;&lt;td&gt;Dream Beans, &lt;em&gt;Hanoi, Vietnam&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Asteraceae, Lemon, Baked Sunflower Seed&lt;/td&gt;&lt;td&gt;April 16, 2023&lt;/td&gt;&lt;td&gt;Vietnam&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Colombia Single Origin&lt;/td&gt;&lt;td&gt;Stumptown, &lt;em&gt;Portland, Oregon&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Baked Apple&lt;/td&gt;&lt;td&gt;April 25, 2023&lt;/td&gt;&lt;td&gt;Colombia&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;I’ve been traveling a lot in the last six months.
My travel setup consists of a grinder, Aeropress, and a small scale, although I’m trying to travel lighter in the future.
These coffee bags more or less represent my travels.&lt;/p&gt;
&lt;p&gt;I’d been talking forever about going to New York, so I decided on a whim to spend February there.
My second day in Manhattan’s Chinatown, I went to the closest coffee roaster and bought
the ‘Coto Brus’ bag from Artbean Coffee Roasters.
It wasn’t exactly my cup of coffee but it helped me through the not-so-cold-to-a-New-Yorker-but-very-cold-to-a-Cali-boy
month of February.
Besides Artbean, I picked up the bag of Devoción beans just a few days before coming back to the Bay Area.
These were probably some of my favorite beans. The Devoción shop in Williamsburg is a nice place but
it does get &lt;a href=&quot;/blog/2023/best-place-for-remote-work&quot;&gt;a bit crowded&lt;/a&gt; on any given day.&lt;/p&gt;
&lt;p&gt;In March, I went to climb in Red Rocks which coincidentally overlapped with my birthday.
We picked up the bag of ‘Bonde’ from Vesta Roasters on the way to the crag one day.
Remote work has enabled me to live the ‘travel + work + climb’ life fairly consistently for
the last year and a half. I’m very lucky to be in a situation where I can
both build a career and pursue my personal passions.&lt;/p&gt;
&lt;p&gt;Speaking of traveling, in late March, I took off on a six week ordeal, going from Taiwan to Vietnam and ending in Japan.
Simple Kaffa was the first of many coffee shops I visited in Taipei.
At times I wished I hadn’t bought that bag, not because it wasn’t tasty but
because I would have liked to spend my caffeine budget checking out the countless
coffee shops of Taipei.&lt;/p&gt;
&lt;p&gt;In Vietnam, I started off with visiting my Aunties and Uncles in Saigon.
Of course, I indulged in my fair share of cà phê sữa.
Then going up north to Hanoi, I learned to produce the illustrious, dangerously rich,
egg coffee.
On my last day in Hanoi, I visited Dream Beans Coffee to try some
Third Wave Vietnamese coffee.
I talked with Long and Duc, the two baristas, about the up and coming third wave coffee scene in Vietnam.
Most coffee grown in Vietnam is Robusta but Dream Beans is bringing attention to Vietnamese Arabica.
Robust is ingrained in Vietnamese culture but the younger folk are starting to take to the
complex flavors of Arabica.
I’m hoping that Vietnam can really take off in the next few years
and join the likes of an Ethiopia or Colombia on the world coffee stage.&lt;/p&gt;
&lt;p&gt;I love talking to other coffee enthusiasts like Long and Duc.
It’s good fun to talk about different brew methods, compare gear,
discuss brew methods, and just share in the joy of our addiction.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;“So what kind of coffee do you like?”, the Japanese man in a white lab coat with curled hair asked me.
I mean, I’m in Japan, most everyone is Japanese.&lt;/p&gt;
&lt;p&gt;“I like coffee that’s more acidic, fruity.”&lt;/p&gt;
&lt;p&gt;“Hmmm okay but like what kind of fruit?”&lt;/p&gt;
&lt;p&gt;“Maybe like cherry or raspberry?” voice wavering a bit, feeling unsure.
“I just like to try local coffee.”&lt;/p&gt;
&lt;p&gt;“Well how about this one?” Curly Coffee Man pointed to square ‘G’ on the menu placard. “It’s a Geisha roasted in Tokyo.”&lt;/p&gt;
&lt;p&gt;“Sure, why not.” I answered half enthusiastic. The price was a bit high, but
the Yen is weak right now right?&lt;/p&gt;
&lt;p&gt;And with that I was off, up to the counter. To coffee man number two.
He was wearing the same white lab coat (must be a uniform) but instead had long
wavy hair swept back and held in place by a rich hair mousse.
And yes, of course he’s Japanese, most everyone in Japan is Japanese.&lt;/p&gt;
&lt;p&gt;“So what’s the ratio that you use?” I asked, attempting to pry into their trade secrets.&lt;/p&gt;
&lt;p&gt;“We used 12 grams of coffee and 192 grams of water.” he said as I watched him.
He tested the water temperature with a kitchen thermometer, then waited a bit more.
“The water should be at 33C… so… you’re American right?”&lt;/p&gt;
&lt;p&gt;“Yeah”&lt;/p&gt;
&lt;p&gt;“Hmmm so I think that’s 92F.” Wavy Coffee Man started to pour from the chrome goose neck kettle.&lt;/p&gt;
&lt;p&gt;“What kind of dripper is that?” I pointed at the conical ribbed metal cylinder.&lt;/p&gt;
&lt;p&gt;“This is a Kalitta, and we also use V60 depending on the type of coffee.”&lt;/p&gt;
&lt;p&gt;“So what kinds of things do you change when you’re trying to dial in?”&lt;/p&gt;
&lt;p&gt;“Sometimes we will change the brew time to bring out different flavors
and to balance that we adjust the coarseness of the grind. And the different cones too.”&lt;/p&gt;
&lt;p&gt;“Ah, I see, so a coarser grind is a longer brew time?”&lt;/p&gt;
&lt;p&gt;“Uh, yes.”&lt;/p&gt;
&lt;p&gt;He was almost done brewing now, just under 3 minutes.&lt;/p&gt;
&lt;p&gt;“So, do you make coffee at home?” finally, it was time to get into home barista territory.&lt;/p&gt;
&lt;p&gt;“Yeah, usually make pour over in the morning.”&lt;/p&gt;
&lt;p&gt;“What kind of brewer do you use?”&lt;/p&gt;
&lt;p&gt;“Oh, its like a hobby so I collect a lot” he said, giving me a laundry list
of brewers “V60, Kalitta, Origami, Chemex. What do you like to use?”&lt;/p&gt;
&lt;p&gt;“Oh! well since I’m traveling right now I have an Aeropress but usually I use a Melitta at home.”&lt;/p&gt;
&lt;p&gt;“Whats Melitta? Is it like Kalitta?”&lt;/p&gt;
&lt;p&gt;“Uh, let me show you.” I pulled up a photo of the classic Melitta dripper on my phone.&lt;/p&gt;
&lt;p&gt;“Ahhhh” Coffee man 3 looked over the shoulder of my barista to take a peak.
I was taken aback, they really hadn’t ever seen a Melitta before? I guess it’s not as common in Japan
as it is on the shelves of Target back home.&lt;/p&gt;
&lt;p&gt;“What about your grinder?” I asked.&lt;/p&gt;
&lt;p&gt;“I use a Comadante.”&lt;/p&gt;
&lt;p&gt;“Oh I heard those are like the best grinders out there.”&lt;/p&gt;
&lt;p&gt;“Yeah I’d hope so.” He laughed. “It was really expensive. But I think
most people don’t need something so expensive. Even if the grind is not even
it can make it more interesting.”&lt;/p&gt;
&lt;p&gt;“How so?”&lt;/p&gt;
&lt;p&gt;“The difference in grind gives more complexity in flavors” he said.
He surprised me with that nugget of wisdom. I had always seen coffee
through the scientific lens: every brew must be perfectly reproducible, any small
variation in equipment or technique would lead to disaster.
But really, even if it’s not a perfect replica every time, coffee is still coffee.
And coffee is delicious.&lt;/p&gt;</description><pubDate>Wed, 07 Jun 2023 00:00:00 GMT</pubDate></item><item><title>In Disguise</title><link>https://enochchau.com/blog/2023/in-disguise/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/in-disguise/</guid><description>&lt;p&gt;When I walk into the restaurant, heads turn to stare. Happens often enough
when I’m the odd one out. Maybe even more so when I’m with a group of my friends.
A group of East Asian dudes in small town White America. Well, maybe not even small town,
just white.
But then I open my mouth and out flows my perfectly slang infused slurred English and
off comes my disguise.
The waiter knows: maybe I’m not from around here, but I’m from a place not too far away.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;“Sorry”. That’s a common phrase I used. Not to apologize for anything important,
just the inconvenience of not understanding the local language.
Usually, a “sorry” is enough to get the person
(from my recent travels: Japanese or Vietnamese) to speak in English,
dashing their expectations and disappointing them to some degree.
I’m just another tourist, this time well camouflaged with slanted eyes and yellow skin, ambiguously Asian.
“I got you, didn’t I? You really thought I wasn’t an American, just because I’m not white.”
I think to myself with a sly smile.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;“Futosu adzuki”, I say raising one finger.&lt;/p&gt;
&lt;p&gt;Kindly, the Tayaki Lady hands me one and I pay.
Otsuka-dono is dying of laughter in the background.&lt;/p&gt;
&lt;p&gt;Yeah, I got &lt;a href=&quot;https://www.tanukiki.com/hitotsu-meaning-japanese/&quot;&gt;that word wrong&lt;/a&gt;.
But I got through that transaction okay. But Lady probably thinks I have some
screws loose.
It probably happened even more times with my broke ass Chinese in Taipei.
And that’s the third disguise, the one that’s harder to see. The shimmer of
the invisibility cloak giving me away. Well, it’s probably much more
obvious than that, my American accent is a real dead giveaway.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;“One chicken curry.” Kodai lists out our order.&lt;/p&gt;
&lt;p&gt;“How spicy?” asks the waiter.&lt;/p&gt;
&lt;p&gt;“Uhhh what are the levels?” The right level of spice is enjoyable, not tolerable.&lt;/p&gt;
&lt;p&gt;“We have mild, medium, and hot.”&lt;/p&gt;
&lt;p&gt;“How spicy is medium?” Always have to double check.&lt;/p&gt;
&lt;p&gt;“It’s Canadian spicy.”&lt;/p&gt;
&lt;p&gt;And in my brain I go “what? wtf is ‘Canadian Spicy’… Ooooh it’s white people
spicy… Oooh yeah, we’re not in the States anymore.”&lt;/p&gt;
&lt;p&gt;But everything in Canada seems so similar and half the time I forget I’m in a different
country. I’m rarely exposed in Canada, but I’m sure there’s always subtleties
that might give me away.&lt;/p&gt;</description><pubDate>Mon, 29 May 2023 00:00:00 GMT</pubDate></item><item><title>I Eat Too Fast</title><link>https://enochchau.com/blog/2023/i-eat-too-fast/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/i-eat-too-fast/</guid><description>&lt;p&gt;I eat like a ravenous hydra, suck down my soup like a vampiric scepter.&lt;/p&gt;
&lt;p&gt;What is food to me?&lt;/p&gt;
&lt;p&gt;Food is just fuel for the body.
Some people like to savor their experience, I like to get mine over with
There’s always room for dessert and limited time in a day.
Let’s move quickly okay? So we have time for dessert.&lt;/p&gt;
&lt;p&gt;Food is something to talk over.
Before consumption, food is a deadly topic for conversation,
a meek battle where democracy meets the hazy mind of the half remembered hungry.
And eventually a compromise is found because, really I’m just hungry and I don’t
care what we’re eating.&lt;/p&gt;
&lt;p&gt;Then during the actual meal, food swaps places, becoming the backdrop to the more important conversation.
Instead of food, I prefer to talk about you, your life, and what’s been happening in it.
The best meals are the one’s that go slowly because we leave time to talk between
bites, creating a natural cadence of ingestion and exhalation.&lt;/p&gt;
&lt;p&gt;“I don’t think I really enjoy my food”&lt;/p&gt;
&lt;p&gt;“Jesus, what the hell you’re already done!?”&lt;/p&gt;</description><pubDate>Tue, 09 May 2023 00:00:00 GMT</pubDate></item><item><title>Cơm tấm</title><link>https://enochchau.com/blog/2023/com-tam/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/com-tam/</guid><description>&lt;p&gt;He stepped out of the Grab and was immediately hit by a cloud of sweet, savory, sticky smoke
that ballooned from the simple charcoal grill.
“That’ll definitely stick for a bit” he thought to himself, walking
up the step into Cơm tấm Nguyễn Văn Cừ.&lt;/p&gt;
&lt;p&gt;”????” the boss lady yelled at him. He couldn’t really make out what she said,
but he was sure it was in Vietnamese so he gave up immediately.&lt;/p&gt;
&lt;p&gt;“Three” but he could barely be heard over the noise of plates clattering and friends chattering.&lt;/p&gt;
&lt;p&gt;“How many people?” she repeated in English.&lt;/p&gt;
&lt;p&gt;“Three people.”&lt;/p&gt;
&lt;p&gt;She motioned with her hand to follow.&lt;/p&gt;
&lt;p&gt;Then Dad stepped up and she continued to talk to him in Vietnamese.&lt;/p&gt;
&lt;p&gt;The three of them, the brothers and their father, were sat down in the corner.
They were sharing a table with three men. Two were quintessentially American,
given away by their size and spoken language. The third (no relation to the Americans)
looked like the lead of a Wong Kar Wai film, complete with slicked back hair
and smart white dress shirt.
His drama lead toughness completed by slowly munching on raw red peppers.&lt;/p&gt;
&lt;p&gt;Menus were passed around and he was surprised to find that there were only seven
things on the menu, all combinations of barbecued meats, broken rice, and add-on
sides.&lt;/p&gt;
&lt;p&gt;“Okay what do you guys want?” Dad asked.&lt;/p&gt;
&lt;p&gt;“I want the pork chop with fried egg”, he pointed at combo number one.&lt;/p&gt;
&lt;p&gt;“Yeah I’ll get a pork chop too.”&lt;/p&gt;
&lt;p&gt;“Whats this?”, he pointed at some words in Vietnamese.&lt;/p&gt;
&lt;p&gt;“It’s meatball”, Dad pointed to the English in parentheses following the Vietnamese.
“I’m going to get the meatball and pork skin.”&lt;/p&gt;
&lt;p&gt;“Okay, I’ll get that too, so for me, pork chop with fried egg and pork skin.”&lt;/p&gt;
&lt;p&gt;“I just want a fried egg with mine.” His brother had always been a little hesitant to try
random foods, especially when he was hangry, which he probably was since they
had just finished a session at the bouldering gym, Vertical Academy.
Perhaps “Sweaty Academy” was a more fitting name. It was a gym built in an old warehouse.
An army of fans blew into the main bouldering area
but they couldn’t overcome the 90F overcast heat and humidity that represented
half of Ho Chi Minh City’s annual climate.
He wondered if his Dad had ever known a day without sweat before coming to the States.
If he had been born to the sweat, was raised in the sweat, and lived by the sweat
until the new government prompted his haphazard departure from the sweat some forty years ago.&lt;/p&gt;
&lt;p&gt;Dad got up to order, using his broken Vietnamese, broken by a lack of usage for the last forty years.
Then they all got up to be moved to another table, away from the English
speakers to a less crowded table at the front of the restaurant.
As they moved, three boys wizzed around them with trays of food and cleaning supplies.
They traveled at break ankle speeds in rubber sandals, setting down food,
picking up plates, wiping down empty tables, and juking out customers, adding to the comfortable level of chaos
that came from the crowded tables.&lt;/p&gt;
&lt;p&gt;Soon enough, the boys were setting things on their table.
First, a tray of wet wipes (2k dongs per use), followed by a teapot with three glass
mugs filled with ice, a paper straw sticking out over the rim.
Finally, the three plates of food and three bowls of fish sauce.
The eyes could finally accompany the nose to appreciate the perfectly charred barbecued pork chop
glistening with fish sauce glaze, sitting on top of half sized grains of broken rice
and shards of pork skin.
Aside it all, the fried egg had ballooned and blistered during it’s deep fry process in
a generous helping of oil.
He drizzled the fish sauce slowly over the rice before giving up and dumping the
rest of the bowl onto his plate.
The first bite was incoming.&lt;/p&gt;</description><pubDate>Wed, 12 Apr 2023 00:00:00 GMT</pubDate></item><item><title>Solo Travel</title><link>https://enochchau.com/blog/2023/solo-travel/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/solo-travel/</guid><description>&lt;p&gt;I’ve been traveling for a bit now, working remote on the side.
I feel more like a local than past tourism experiences since I don’t have as much time to kill.
I’m not spending my days museum hopping, visiting historic sights, or taking photos at land marks.
But that’s fine since remote work is more ideal for solo travel since I can move on my own schedule.
I can wake up early or late, work a bit, take the subway to check out a restaurant I saw on Taiwanese YouTube,
work at a coffee shop for a few hours, then check out a new climbing gym elsewhere in the city, and maybe work a bit more.
Every day I wake up, pull out my compass and spyglass, roll out the Google Maps, and chart my course.
This has helped me build self reliance. I’ve been learning what I do and don’t like to do and
how to find place I do and don’t like to go.
But while planning everything myself is pure freedom, I do miss having someone to talk to.&lt;/p&gt;
&lt;p&gt;I miss having someone to talk
when I’m having a meal,
when I’m taking the subway,
when I’m waiting between sets at a show,
when I’m sitting in a coffee shop,
when I’m having my first drink,
and in every other moment of boredom.
Not only is that someone a conversation partner, they’re also a full
fledged human being with their own interests that they can share with me.
I’m down to try new things, new things that I have never thought of before.
Two brains can think of more things than one.
Sharing is caring, but sharing is also cheaper, because I get to share your wallet,
and of course, you get to share mine.
You can fit a whole lot of people into a bus.
You can also fit a whole lot more family style dishes into four stomachs than three, and three stomachs than two, and two stomachs than one.
The more internal organs, the better.&lt;/p&gt;
&lt;p&gt;Of course I can meet new people during my travels but I’m a little shy.
A fisherman learns to catch fish by observing the behavior of fish.
In my case, I learned to drink like a fish.
I’m half certain that my Chinese gets better when I’m drunk, not to mention that my
inhibitions are inhibited by neurological inhibitors in liquid form.
So I can have some fun, talk to some new people, make small talk.
But these people I meet, I know I’m likely never going to see them again.
The amount of time I spend in a place is inversely correlated with the activation
energy threshold necessary to get myself to talk to strangers.
But strangers can become new friends, and new friends grow into old friends
and who knows where the world will take you.&lt;/p&gt;</description><pubDate>Sat, 08 Apr 2023 00:00:00 GMT</pubDate></item><item><title>Hello Taipei</title><link>https://enochchau.com/blog/2023/hello-taipei/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/hello-taipei/</guid><description>&lt;p&gt;It’s nice to be back in Taipei. The last time I was here was
the summer of 2019, right before the pandemic.&lt;/p&gt;
&lt;p&gt;I slept like shit on the plane so I’ve been treating myself to nice coffee
to maintain my energy levels.
So far today, I’ve been to two coffee shops: Simple Kaffa and Peloso Coffee Raoster.
I’ve never been to a sit-down cafe before but both of these shops are just that.
The experience is more similar to a restaurant. Someone, either the host or a barista,
greets you at the door and seats you. Then they take your order and bring you the
coffee.
That’s pretty different than the counter style, seat yourself experience I’m accustom to
in the US.&lt;/p&gt;
&lt;p&gt;Another difference is the attention that brewed coffee gets over espresso.
American coffee culture is very much entrenched in cappuccinos and lattes.
In Taipei, at Simple Kaffa, their primary offering was a V60 pour over.
At Peloso, you could choose between a V60 pour-over, the Abid Clever, or Aeropress.
That’s the first time I’ve ever seen Aeropress coffee offered at a coffee shop.
I like the focus on brewed coffee.
I’ve come to notice that most cappuccinos all taste about the same and the
best judge of a coffee shop is how good their drip or pour-over is.&lt;/p&gt;
&lt;p&gt;Talking to wait staff and customer service is always a little fun.
I play a game with myself where I try to see how long it takes them
to realize I’m not Taiwanese.
I think it comes pretty fast.
Back at UC Irvine, I could tell the Chinese international students apart
just by the way they dress and I’m sure I stick out a bit in Taiwan through
my American fashion sense.
Then I open my mouth and my American accent is a dead giveaway.
It’s weird being ABC because people expect you to know Chinese.
I don’t get the White foreigner card.
I’m always making errors when speaking to customer service.
Sometimes they talk too fast for me and I can’t make out what they’re saying
or they use vocabulary I’m not familiar with.
Usually I have an idea of how the interaction will take place.
You know, like the simple exchange of:&lt;/p&gt;
&lt;p&gt;“What do you want?”&lt;/p&gt;
&lt;p&gt;“I want this one (這個)”&lt;/p&gt;
&lt;p&gt;But then the conversation deviates from the standard dialogue and I’m caught off guard.
This whole experience makes me more empathetic towards my immigrant parents and grandmother.
I get embarrassed since I feel like I should be able to understand, but I can’t.&lt;/p&gt;
&lt;p&gt;Being mostly illiterate kinda sucks. I try my hardest to read as much of each
sign as I can but I usually only get the gist of it.
I’m most disappointed that I’m not able to fully read the menus at the small
diner restaurants where everything is plastered to the wall and has been
there for the last 40 years. The only thing that’s changed is the prices at the bottom
which have been covered many times with paper and tape to reflect the cost of inflation.&lt;/p&gt;
&lt;p&gt;These small diner restaurants are everywhere. Taiwan (and probably most countries)
is incredibly homogeneous. There’s like the big chubby Asian dude and the small
glasses wearing Asian dude. All the girls wear one of three makeup styles: Japanese,
Korean, or Western.
And with homogeneity, comes a cap to the diversity of the city.
I find the same types of restaurants every third block.
The differences are there but they are not pronounced to my bleary eyed
American mind that’s used to only having to tell the difference between
Japanese and Mexican food and not the nuances between the cooking of this 阿媽 or that 阿媽.
Speaking of which, I tried Mexican food in Taiwan once back in 2017.
It was by far the worst Mexican food I’ve ever had.&lt;/p&gt;
&lt;p&gt;I suppose you trade diversity for safety.
I’m not sure why but it just feels safer in Taipei than anywhere I’ve been
in the US.
Perhaps when racism isn’t an issue then there’s just fewer things
for people to fight over so there’s less conflict overall.
And also you can’t easily own a gun.
But hey, we got bomb ass tacos in the States.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Oh yeah, I also have no idea whose still in Taipei so if you’re here, hit me up!
I desperately crave human attention.&lt;/p&gt;</description><pubDate>Wed, 29 Mar 2023 00:00:00 GMT</pubDate></item><item><title>On Cars, the Subway, Closeness, and Communication</title><link>https://enochchau.com/blog/2023/cars-subway-closeness-communication/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/cars-subway-closeness-communication/</guid><description>&lt;p&gt;Max’s glass was empty. It had been empty for the past hour or so.
I was babysitting my spiked melon soda, still a little tipsy from our last drink
up in Midtown.&lt;/p&gt;
&lt;p&gt;I tried to avoid her gaze as the waiter came around again. She looked well
kept in a 1950’s boyish kind of way, her short hair was slicked back with grease, dark mascara lined
her eyes. As she stopped, I could feel my chest grow heavy with the weight of a thousand rats.&lt;/p&gt;
&lt;p&gt;“How are you two doing? Can I get you another drink?” she asked pressingly.
I could feel that she wanted us to leave.&lt;/p&gt;
&lt;p&gt;“Yeah we’re fine”, Max responded, seeming to miss a bit of the tension I felt.&lt;/p&gt;
&lt;p&gt;“Actually, can we get the check?”, I needed to defuse my anxiety.
It was time to end the night.&lt;/p&gt;
&lt;p&gt;“Sure I’ll get it right away.” She seemed to completely disregard my presumed notion.
She smiled at us as she walked away. Every step she took felt like another
rat falling off my sternum.&lt;/p&gt;
&lt;p&gt;She came back, bringing around the card reader.
As if a magician’s sleight of hand, I somehow found my credit card already in my palm.&lt;/p&gt;
&lt;p&gt;“Any other plans for tonight?” She looked quizzically at Max.&lt;/p&gt;
&lt;p&gt;“We’re gonna get Joe’s after this. It’s actually his last weekend in the city.”&lt;/p&gt;
&lt;p&gt;“Oh where are you from?”&lt;/p&gt;
&lt;p&gt;My turn now, “I’m from the Bay Area, actually we both are. I’ve been here for
about a month now, he’s been here for two…”&lt;/p&gt;
&lt;p&gt;She widened her eyes. “Oh! California! I’m originally from LA.”&lt;/p&gt;
&lt;p&gt;“Well he’s thinking about moving here.” I gestured at Max.&lt;/p&gt;
&lt;p&gt;“Well you should definitely give it a shot.” She paused to think.
“I moved here in 2021, so it’s been 2 years now. You know, coming from LA, I like
the subway and I don’t miss driving but there’s something I do miss.”&lt;/p&gt;
&lt;p&gt;“Oh what’s that?”&lt;/p&gt;
&lt;p&gt;“I miss being alone in my car. Here in New York, you’re always surrounded by people
on the subway.”&lt;/p&gt;
&lt;p&gt;“Yeah, I get it, you miss that feeling of solitude.” Since our first time meeting
up in New York, Max would occasionally bring up a feeling of suffocation he felt in the city.
He was confounded by the noise of sirens and horns in the morning that drowned out
the chirp of birds, the suffocating feel of car exhaust mixing with the smell of garbage piled on the curbs, and
the gray and brown concrete that seemed to hide every last bit of greenery.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;BEEP&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I touched my card and the sound came through.&lt;/p&gt;
&lt;p&gt;Getting up, Max replied “Well it was great meeting you”.&lt;/p&gt;
&lt;p&gt;“Yeah! You guys have a great night.” Once again she walked away. A few more
rats came tumbling off my solar plexus.&lt;/p&gt;
&lt;p&gt;It was back into the cool crisp February air.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;It’s true that the subway packs us together. Sometimes, during rush hour, it can
feel like you’re surging forward in a crowd of attentive concert goers,
but you’re really just pushing to get on the A train so you don’t miss
your date at the West 4th St. - Washington Square station, but of course Downtown is
closed because of some random track work MTA is cooking up so you have to go
two stations up before taking the express back down, which is packed full of people
on the same adventure you’re on.
Nevertheless, in a Stockholm-y kind of way, I feel a closeness to those people shoved up around me.
I can see them (and smell them) as people, imagine what their day must have been like, and stare just
a little too long to make awkward eye contact before quickly looking away.&lt;/p&gt;
&lt;p&gt;That’s a large difference from what it feels like to drive a car.
In a car, all humanity is hidden beneath the colorful metal shell, your life and cargo
protected by the fringe bonds of society and the fear of a higher insurance premium.
Our only means of communication are the movements of the vehicle and the blare of
the horn, both which can only go so far as to create feelings of confusion, annoyance, or anger.
Day after day of sitting in traffic and being exposed to this hostility must have
detrimental effects on the mental health of car-centric America.
It’s a wonder our country hasn’t divided neatly into two parties of red and blue that desperately want
to rip each other to shreds. Oh wait.&lt;/p&gt;
&lt;p&gt;I want to quote now &lt;a href=&quot;https://blog.matsu.io/on-leaving#why-leave&quot;&gt;a blog post&lt;/a&gt; by &lt;a href=&quot;https://blog.matsu.io/about&quot;&gt;Pine Wu&lt;/a&gt;,
a former developer on Visual Studio Code and prolific open source maintainer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have certain beliefs. They are hard to justify. For example, I hate cars.
I believe cars bring hard-to-quantify damage to a city.
I believe cities with a lot of cars are terrible.
I can’t really give you a correlation between walkability and happiness, neither can I prove cars bring more harm than benefit to a community.
I just believe massive amount of cars make people’s life terrible. Both the pedestrians’ life and the drivers’ life.&lt;/p&gt;
&lt;p&gt;It saddens me that many are not aware of other alternatives and what cars cost them.
I have been to Kyoto, where people mostly walk.
I went to Copenhagen, where people mostly bike.
I traveled to Wuzhen, where people transit with boats.
I was in Zürich, where most people take trams.
These cities share a quality that I cannot point finger to but enjoy nevertheless.
And cars are not compatible with that quality.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have certain beliefs that are just as hard to justify.
I believe that with communication technology (text messages, cell phones, and video chat), like cars,
we have gained convenience but lost an aspect of human communication.
The minutiae of communication: a twitch of the knee, a sideways glance, a frown or a smile, is lost
when transferred on invisible waves and underground wires.&lt;/p&gt;
&lt;p&gt;I’ve been reading more fiction lately and one thing I appreciate is that it’s easy
to tell what a character feels during a conversation by the bits of interspersed
description between pieces of dialogue. With texting, so much context is left out.
It’s so hard to be sarcastic over text without something like the &lt;code&gt;/s&lt;/code&gt; symbol to
denote the intended tone. The reader is left to fill in the blanks that are
normally occupied by body language and tone of voice, in a vain attempt to
recreate the original intent of the author.
Thankfully, we do have emojis and ascii smiles but that’s barely a start, to stand-in for a billion years
of evolution that enabled humans to detect body language.
Maybe we should all just add a bit of flowery prose at the end of each text message
to really get across what we mean.&lt;/p&gt;
&lt;p&gt;The internet is truly a blessing that has connected our world, but it is
only a stepping stone for creating long lasting relationships.
Any meaningful relationship will eventually leave the confines of the virtual world
to embrace in loving arms and feel the closeness of chest against chest and
the subtle syncopation of heartbeats as we gaze longingly into each others eyes.&lt;/p&gt;</description><pubDate>Thu, 09 Mar 2023 00:00:00 GMT</pubDate></item><item><title>The Best Place For Remote Work in 2023</title><link>https://enochchau.com/blog/2023/best-place-for-remote-work/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/best-place-for-remote-work/</guid><description>&lt;p&gt;Ever since I joined the workforce, I’ve always been working remote.
Normally, at home, in the Bay, I have a little desk setup in my room but
for the last four weeks I’ve been living in a closet in Manhattan that had
a sub-optimal office setup.
I’ve been going out everyday to find a nice spot to work from.&lt;/p&gt;
&lt;p&gt;Whats the first thing that comes to mind when you think of a place to do work?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Opening the door he was met with a low din of human voices, espresso grinders
whirring away, and the hiss of milk being foamed for a fresh cappuccino.
The smell of coffee beckoned him down the long wooden hall into the large spacious
atrium. Built into the walls on either side were wooden bars where
a variety of stylish twenty something office workers huddled on their macbooks.
The centerpiece of the room contained two aging brown leather couches, meant to evoke
a living room vibe. Spiraling outward from the couches were small two person tables,
perfect for a coffee chat, but most were only occupied by single remote office
worker who had likely been at that same desk since coming in for their morning
coffee at 9AM.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, it’s true, most coffee shops do have free WiFi and ample seating.
The issue is that you’re competing with all the other local remote office
workers who just need to find a trendy spot to do their work.
In my experience, I might not be able to find a seat when I first come in,
so I stand around awkwardly waiting while my coffee gets cold.&lt;/p&gt;
&lt;p&gt;Since the pandemic and the proliferation of remote work, many cafes have begun
to adopt a full or partial “no laptops” rule.
For good reason too, because people who work at coffee shops stay there way too long.
These remote workers take can take up a table for 4-5 hours at a time, maybe staying the whole
day. They take up space that might otherwise go to local neighbors who just
want a place to hang out, to two new co-workers on an informal coffee chat, or to
the Korean tourist couple who want to try the best coffee in Williamsburg.
Coffee shops are meant as a place for community to gather.&lt;/p&gt;
&lt;p&gt;So what is the best place for remote work in 2023?
Think back, where did you like to go to study in college?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Like a castle tower, the spire of the Jefferson Market branch towered above him.
He opened the old oak double doors to step into the coolness of the old stone
castle. To his right a spiral staircase snaked in two directions, up into the spire and down
into the dungeon. In the main reading room, natural light filtered in from the
large mosaic windows. He was greeted by the smell of old paper from the
books, sitting on the shelves that ringed around the room.
A variety of local neighbors sat at three long tables, which gave the feeling of a grand feast,
but it was dead silent besides the occasional tap of a laptop keyboard or the turn of a page.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Libraries come with a certain guarantee of quality provided by the municipal government.
The desks are made with work in mind, the tables have builtin outlets
to keep your laptop charged, the WiFi is fast, and the patrons are encouraged
to make the least amount of sound possible. Desk space is also generally plentiful.
It’s the perfect place to get deep work done.&lt;/p&gt;
&lt;p&gt;The one issue I did run into with library work is that I couldn’t take meetings
there. But it’s not a huge issue. Anytime I did have a meeting, I’d pop
out to the nearest coffee shop, hop on their free WiFi, and leave just as quickly.
No one complains if I don’t buy anything and I’m not worried about finding a seat
since I know I won’t be there for long.&lt;/p&gt;
&lt;p&gt;Lately, I’ve been missing that feeling of community that comes with having
friends from all walks and ages of life. At the library, you can kind of get these
vibes. There’s old people sitting next to bums, surfing on the
web on the free computers; students studying for their upcoming exam; and
nannies taking toddlers out for story time.
It’s a nice diverse crowd and I got to see what normal New Yorkers look like,
not just the trendy transplants who hang out in Brooklyn.&lt;/p&gt;
&lt;p&gt;Give it a shot. If you’re a remote worker and you like to work from coffee shops,
maybe go to your local library for a change.
Maybe you’ll see a flier for an upcoming cool community event you’re interested in.
Maybe you’ll find some fiction to pull you off your phone for a few hours.
Maybe you’ll even have a picturesque meet-cute as you gaze across the room,
locking eyes with your future lover.
The possibilities are endless.&lt;/p&gt;</description><pubDate>Fri, 03 Mar 2023 00:00:00 GMT</pubDate></item><item><title>Tech Bros</title><link>https://enochchau.com/blog/2023/tech-bros/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/tech-bros/</guid><description>&lt;p&gt;Let’s get incredibly stereotypical.&lt;/p&gt;
&lt;p&gt;Who is a tech bro?
Well, they’re pretty easy to spot, just look for the mid twenties White or Asian male
wearing a Patagonia zip up with a corporate logo atop joggers or jeans ending
in All Birds wool runners.
If you manage to inject a few probing questions into their anti-social bubble,
you might hear words like FAANG or startup followed by some ridiculously large
six figure number that could be referring to either their total compensation
or penis size in micrometers&lt;/p&gt;
&lt;p&gt;Every tech bro starts somewhere, and somewhere happens to be middle class
America.
In this setting they spend their decade of adolescences behind a screen,
playing video games and surfing the web,
foregoing normal social activities like clubs, sports, music, and arts.
They don’t have much going for them outside of the virtual world.&lt;/p&gt;
&lt;p&gt;Then comes college.
They’ve spent their whole life in front of the screen, so it’s no wonder they’d
want to pick a career path that allows them to continue spending every waking hour
staring at dots of light move across a glass display.
Plus it’s good money and there’s prestige in it.
Everyone is wowed when they slyly remark “I work at &lt;em&gt;FAANG company&lt;/em&gt;”.
Finally, something to be proud of, something to be recognized for.
So they wrap their whole personality around the one thing they have that
seems to have intrinsic value to the outside world.&lt;/p&gt;
&lt;p&gt;Spending their teenage years in front of the computer leaves little time to
form social skills, like making friends, much less talking to girls.
So back to the internet, to search for a solution, “How do I lose my vir…
&lt;em&gt;backspace&lt;/em&gt; &lt;em&gt;backspace&lt;/em&gt; &lt;em&gt;backspace…&lt;/em&gt; How do I get girls?”.
They live a life dictated by Reddit users who have gone out into the world and are back
to report their findings so that they don’t have to venture out into the cruel, darkness
of non-virtual reality.
Back in non-virtual reality they come up with generic hobbies like
hiking, climbing, lifting, boba, and hotpot along with a generic dress style like
the &lt;a href=&quot;https://www.reddit.com/r/malefashionadvice/&quot;&gt;r/malefashionadvice&lt;/a&gt; uniform and
gorp core pieces like Arc’teryx and Patagonia that they use only for their office
commute.
Just ask your female friends what they see on Hinge in the Bay Area.&lt;/p&gt;
&lt;p&gt;So they wake up in their $5k/month luxury apartment in downtown San Francisco
which was built just last year, replacing the old apartment block that mysteriously
burned down. Coming down the steps, they dodge around the homeless man taking a shit and the
drug addict smoking heroine to hop on their &lt;em&gt;FAANG company&lt;/em&gt; corporate bus
to take them on their 1.5 hour long ride to their corporate office in Silicon Valley
where they spend the day enjoying free lunch and other perks, simultaneously trying to do
the least amount of work possible and seem as busy as possible, all so they can meet their KPIs
and get a glowing performance review at the end of
the quarter which will hopefully translate to a nice fat bonus with which they
can finally afford to unlock the full self driving feature of their Tesla
Model 3 that they pay $300 a month to park in their San Francisco luxury apartment’s parking garage
where it can be safely kept until getting it’s windows smashed on the one day they decide to
go to that new climbing gym that just opened in Oakland.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;If you have a good sense of humor, you’ll probably find this funny.
If you don’t find this funny, maybe you should reevaluate how your life is going.&lt;/p&gt;
&lt;p&gt;best wishes,&lt;/p&gt;
&lt;p&gt;E&lt;/p&gt;</description><pubDate>Thu, 23 Feb 2023 00:00:00 GMT</pubDate></item><item><title>A Winter House Warming</title><link>https://enochchau.com/blog/2023/a-winter-house-warming/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/a-winter-house-warming/</guid><description>&lt;p&gt;“This is Kasey, and this is Carey”&lt;/p&gt;
&lt;p&gt;“Hi I’m Enoch, nice to meet you. So Carey, this is your place then… Wait you’re both Carey?”&lt;/p&gt;
&lt;p&gt;“Oh no, my name is Karen. You can also call me KC, those are my initials.”&lt;/p&gt;
&lt;p&gt;“No one calls you that”&lt;/p&gt;
&lt;p&gt;“Shut up”&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;“Wait so you moved here in October but you’re having the house warming party now?”&lt;/p&gt;
&lt;p&gt;“Yeah, well, I finally got this mirror and its the last thing I’m going to get for awhile”&lt;/p&gt;
&lt;p&gt;The room was neatly decorated in a nouveau Scandinavian style.
A three piece couch of soft curves melting into tapered stilts sat around a low
wooden coffee table smoother than any human touch could ever sand, edges shaped
to digital perfection.
Against the opposite wall stood a television nearly as wide as he was tall,
the latest sound bar, in white, emanating a rising, falling, consistent click on the twenty-fourth track.
All this rested above a similarly modern yet similarly artificial low oak cabinet.
And in the corner, the final set piece the full length mirror, framed in black,
as if it were a painting about to go up on the matte white gallery like walls.&lt;/p&gt;
&lt;p&gt;He couldn’t help but wonder if it would all still be here in a years time, maybe
two years time.
He knew too many of his twenty something peers with beautifully decorated apartments,
abandoned within a years time, maybe two years time.
His mentality still remained in vagabond mode, only enough possessions necessary
to make the next move less painful.
And in that mental trench, he could not imagine calling somewhere home that was not
his childhood home.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;“But I don’t think I’d want to do engineering long term.”&lt;/p&gt;
&lt;p&gt;“Oh, what would you do instead?”&lt;/p&gt;
&lt;p&gt;“I’d go back to teaching. I don’t actually have a CS degree, I have a teaching degree.”&lt;/p&gt;
&lt;p&gt;“Oh so you have a teaching credential, for what subject?”&lt;/p&gt;
&lt;p&gt;“Math”&lt;/p&gt;
&lt;p&gt;“In which state?”&lt;/p&gt;
&lt;p&gt;“It’s for New Jersey, yeah, I was a student teacher for math and computer science
in college. I didn’t learn CS until college but when I did, it made so much sense to me.
Like it was so similar to math.”&lt;/p&gt;
&lt;p&gt;“So how’d you end up at &lt;em&gt;*big tech company&lt;/em&gt;* ?”&lt;/p&gt;
&lt;p&gt;“Well I mean, the salary kind of got me. But Software engineering is so isolating,
I don’t think I could get that into architecting code. I’d rather do something that involved people.”&lt;/p&gt;
&lt;p&gt;“Yeah, teaching is a really rewarding experience. I have a lot of respect for teachers.”&lt;/p&gt;
&lt;p&gt;More and more, the people trickled in, like a blood infusion, giving life to the party.
In response, he forced death upon himself, in the form of liquid poison, to unearth
the charm buried under years of unresolved emotional trauma.
In that addled state, he couldn’t fully grasp the conversation he had just had,
until the next day, on the subway, that he could finally connect
that conversation to the common conversations he had back home:&lt;/p&gt;
&lt;p&gt;“I’m thinking about going to a software bootcamp.”&lt;/p&gt;
&lt;p&gt;High paying, stable growing industry, plentiful jobs;
all hallmarks of a career blessed by Asian parents.
And yet, he had heard too many stories of his peers quitting the industry or
planning to quit the industry or looking for a way to retire early.
Because there’s something more to life than making more money than you know
what to do with at a job you can barely tolerate.&lt;/p&gt;</description><pubDate>Mon, 13 Feb 2023 00:00:00 GMT</pubDate></item><item><title>Hello New York</title><link>https://enochchau.com/blog/2023/hello-new-york/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/hello-new-york/</guid><description>&lt;p&gt;I’m finally out in New York for the month of February.
I feel like I’ve been talking about going for the better half of 2022
and it finally happened.
The last time I was here was more than 10 years ago, back when I was a little kid.
I’ve been here for four days now and I want to give my first impressions.&lt;/p&gt;
&lt;h2 id=&quot;food&quot;&gt;Food&lt;/h2&gt;
&lt;p&gt;I love going to bakeries.
There’s a certain kind of store that &lt;a href=&quot;https://www.joannwphotos.com/&quot;&gt;Joann&lt;/a&gt; says is my kind of store, places
like &lt;a href=&quot;https://www.backhausbread.com/&quot;&gt;Backhaus&lt;/a&gt; and &lt;a href=&quot;https://www.farmsteadbakery.com/&quot;&gt;Farmstead&lt;/a&gt;.
The thing about New York is that there’s one of these places on every other
block around where I live.
And then there’s like all the other boroughs that I haven’t explored yet.
I’ve read endless articles about “Best Bakeries in New York” and every list
is a bit different.
Maybe I haven’t looked around San Francisco enough but I feel like the density
of good food here is just so much higher.&lt;/p&gt;
&lt;p&gt;Today’s bakery was &lt;a href=&quot;https://www.melthebakery.com/&quot;&gt;Mel the Baker&lt;/a&gt;.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_mels_chocolate_croissant.DFzI4F82_ZubaCM.webp&quot; alt=&quot;mel chocolate croissant&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;blockquote&gt;
&lt;p&gt;He bit into the crispy carmel shell of the chocolate croissant.
It crumbled in his mouth and gave way to the gooey chocolate innards that oozed out,
coating his tongue.
A totally new experience than the al dente chocolate he was used to.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;img src=&quot;/_astro/01_mels_bread.Yv6tcNOE_Z1R6tlo.webp&quot; alt=&quot;mel bread&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;CLANG&lt;/em&gt;. The knife bounced against the turtle like shell of the country loaf.
He would have to work a little harder to expose the soft fleshy sourdough hidden beneath.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Besides bakeries, there’s also tons of good restaurants here.
There’s good cheap eats, there’s good fancy eats, and everything in between.
One fun part of traveling is finding good local food.
Well, there certainly is no shortage of that in New York.
I’ve gotten endless lists of Google Maps pins to the best restaurants in New York.
I thought I might cook here but it almost feels like a waste of calories
not to try a new restaurant for every meal.
I know it’s expensive but my excuse is that I’m traveling.&lt;/p&gt;
&lt;h2 id=&quot;groceries&quot;&gt;Groceries&lt;/h2&gt;
&lt;p&gt;There’s not too many big chain grocery stores near where I live in China Town.
There are a fair amount of Chinese grocery stores near me.
I saw this Chinese grocery store in a &lt;a href=&quot;https://youtu.be/UkNdqnAVDKE?t=84&quot;&gt;Bon Appetit video&lt;/a&gt;
a few months back and I joked to myself about going to find it.
Jokes on me, I live right next to it.&lt;/p&gt;
&lt;p&gt;I’m not so used to shopping at Chinese grocery stores.
The produce section, dry goods, and frozen foods sections I’m okay
but the meat isn’t prepackaged.
I’ve never bought meat from a butcher before.
I’m a bit intimidated because I don’t really know the different parts of cows and pigs.
Well, there’s always the Trader Joe’s that’s a few blocks away.&lt;/p&gt;
&lt;h2 id=&quot;public-transit&quot;&gt;Public Transit&lt;/h2&gt;
&lt;p&gt;My Hinge profile says that one of my life goals is to “live in a walkable city
with good public transit”.
New York ticks that checkbox.
Come to think of it, I should probably take that off my profile since it’s a bit
redundant given my current location.
But yeah, everything is about a 20 minute subway ride away, plus maybe a short walk.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_walking_graph.BCjfssVg_Z2nBLtE.webp&quot; alt=&quot;walking graph&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;599&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Look at those measly bars from the Monday and Tuesday before I came to New York.
Since arriving, I’ve been walking more than 5 miles a day.
Granted, I’m trying to make the most of my time here so I go out every day.
But still, I think the last time I regularly walked this much was back in college.&lt;/p&gt;
&lt;p&gt;On my first day, I took two wrong trains.
Also, sometimes there’s no reception on the subway so there was
another moment when I almost got lost again because I didn’t know which stop
to get off on.
Andy’s pro-tip was to get Google maps loaded beforehand.
I’m learning the lines slowly but for now, Google maps is my version of Jack Sparrow’s compass.
I look &lt;a href=&quot;https://www.youtube.com/watch?v=dlXSJ83T9Lg&quot;&gt;like him&lt;/a&gt; too,
pointing my phone in different directions, trying to figure out which way to walk.&lt;/p&gt;
&lt;h2 id=&quot;fashion&quot;&gt;Fashion&lt;/h2&gt;
&lt;p&gt;The average person dresses much better.
There’s a more diverse crowd which leads to more diverse styles.
On the two extremes, you’ve got the artist that keep up with fashion trends
versus the finance bros wearing tailored suits and wool coats.
It’s subtle but it’s noticeable that everyone dresses a little bit better.
Compare that to San Francisco, where everyone works in tech so they’re either
wearing gorp, techwear (like &lt;a href=&quot;https://www.youtube.com/c/TechLead&quot;&gt;Tech Lead&lt;/a&gt;
style techwear, not actual techwear), lounge wear (because they never leave their house),
or the &lt;a href=&quot;https://www.reddit.com/r/malefashionadvice/&quot;&gt;r/malefashionadvice&lt;/a&gt; uniform
because it’s their first time figuring out how to dress.
Okay, I’m ragging on tech bros, but it do be like that.&lt;/p&gt;
&lt;h2 id=&quot;night-life&quot;&gt;Night life&lt;/h2&gt;
&lt;p&gt;There’s always something to do.
You can find tickets to a show any day of the week.
I’ve been to a comedy club so far, and I’m planning on doing jazz clubs and
maybe a show on Broadway as well.
I went out drinking with &lt;a href=&quot;https://maximchen.com/&quot;&gt;Max&lt;/a&gt;, Hans, and their friends on a Thursday and the bar
was packed.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_whisky_tavern_thursday.Po89wQ97_Z2t8dOi.webp&quot; alt=&quot;whisky tavern thursday night&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The most surprising thing is that places are actually open late.
You can grab a bite to eat past 9pm, amazing!
Back home in the Bay, everything closes at 9pm.&lt;/p&gt;
&lt;h2 id=&quot;the-homeless&quot;&gt;The Homeless&lt;/h2&gt;
&lt;p&gt;The homeless people seem nicer in New York.
I have yet to see someone smoking heroine in public.
I have yet to see shit that looked obviously human (there’s a fair amount of dog shit though).
I have yet to see a tent village.
Well, I have seen a dude who was probably jerking off at the library, but
I have yet to be yelled at or harassed by a homeless dude.&lt;/p&gt;
&lt;p&gt;Some dude got in my train car today and announced
“Hello everyone, I’m homeless, does anyone have $3 to spare so I can ride the train again later?”
And no one gave him money, and he didn’t get upset.&lt;/p&gt;
&lt;p&gt;To be fair, it’s only been 4 days, compared to 20 years in the Bay Area
but at least so far, the homeless people seem way more chill.&lt;/p&gt;
&lt;p&gt;I personally think it’s because New Yorkers aren’t willing to take shit
from crazy people.
There’s a sense of “this is my home and I’ll take care of it” compared
to the rotating cast of tech transplants in San Francisco who could care less
about a city they only expect to spend a few years in.&lt;/p&gt;
&lt;h2 id=&quot;hes-leaving-home&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=VaBPY78D88g&quot;&gt;He’s leaving home?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve been ragging a lot on San Francisco and it’s tech bros in this post.
It’s the easiest thing for me to compare New York against.
But to be fair, those tech bros are me and my friends and
despite all of San Francisco’s follies, I couldn’t see myself leaving the Bay long term.
New York City is a dream of a city, but that’s it, it’s just a city.&lt;/p&gt;
&lt;p&gt;My focus in life has slowly pivoted away from the city and towards the crag.
There’s not much outdoor climbing out here, and it’s hard to justify owning a car,
with such good public transport, just to use it to drive out to the crag.
The cold doesn’t bother me too much but it’s definitely discouraged me from going out.
Compare that to the consistent 60F weather in the Bay, the endless hiking, and
collection of outdoor crags.
Case in point, I’m flying back to San Francisco during President’s Day weekend just to go climb in Bishop.&lt;/p&gt;
&lt;p&gt;I can’t have my cake and eat it too.
I can’t have good public transit and realistically own a car.
I can’t experience endless alcoholic night life if I want to push my physical limits on the rock.
I can’t spend a day out in nature if it’s below freezing.&lt;/p&gt;
&lt;p&gt;Oh the things we do for the things we love.&lt;/p&gt;</description><pubDate>Sat, 04 Feb 2023 00:00:00 GMT</pubDate></item><item><title>Walking Chinese San Francisco</title><link>https://enochchau.com/blog/2023/walking-chinese-san-francisco/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/walking-chinese-san-francisco/</guid><description>&lt;p&gt;&lt;em&gt;Noriega &amp;gt;&amp;gt; Irving &amp;gt;&amp;gt; Golden Gate Park &amp;gt;&amp;gt; Balboa &amp;gt;&amp;gt; Golden Gate Park &amp;gt;&amp;gt; Noriega&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I take a brisk walk, two blocks, down the hill to the dentist office on the corner.
I’m rushing, I’m five minutes late, I hate being late, I don’t want to let down
the dentist or the receptionist, especially after the bombardment of reminders in the
weeks leading up to my appointment.&lt;/p&gt;
&lt;p&gt;My dentist today is Dr. Lee. It’s always a different dentist.
The head dentist who owns the practice has a rotating cast of trainees and recent
graduates that come through, never staying more than the 6 months between my appointments.
She tries to talk to me.
I get it, it’s good to build a doctor and patient relationship, but again,
I’ll probably be seeing someone new in 6 months.&lt;/p&gt;
&lt;p&gt;I’ve been at the same practice for the last 20 odd years, the same dentist, Dr. Louie.
His daughter, Dr. Jackie, might take up the practice after he retires so maybe
I’ll still be here in another 20 years.&lt;/p&gt;
&lt;p&gt;The place hasn’t aged a day since my childhood, besides the introduction of some
plastic shields, following standard COVID procedure.
Still the same four waiting room chairs, none of which I’ve ever seen fully occupied.
Still the same over head light, above the same electric dental chair, although
this time I notice the rusted bolts on the light’s crane arm.
Intrusive thoughts invade my mind as Dr. Lee prods my gums.
Twenty years has to be a lot of wear… I wonder if that thing might fall off
it’s adjustable track and crush my balls.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_sunset_walking.63UaGV_h_Zr9Rt3.webp&quot; alt=&quot;sunset walking&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;It’s lunch time.
My dental insurance doesn’t cover fluoride anymore so no more waiting a half hour
before I can eat.
Good thing since it’s only a 20 minute walk down to Irving.
No location in mind, just looking for something good to eat.&lt;/p&gt;
&lt;img src=&quot;/_astro/01_irving.Bz3SeJKX_Zs3J3L.webp&quot; alt=&quot;Irving St&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The Sunset district is kinda like San Francisco’s second Chinatown.
Noriega is one part, Irving is another, Judah and Taraval round it out.
I stick out a bit, the one younger Asian dude in a throng of older Chinese folk.
A lot of people are out today, likely shopping for the upcoming Lunar New Year.&lt;/p&gt;
&lt;p&gt;Passing by Uncle Benny’s Donuts, I stop into Pho Huynh Hiep 2 (a.k.a Kevin’s Noodle House).
My usual order is vermicelli with bbq pork and egg rolls.
My grandma would make this same dish for us when I was growing up (sans the egg rolls).
My dad would come home with ziploc bags full of white noodles, tupperware containers
of bbq beef and shrimp, fresh mint and herbs in sandwich bags, homemade fish sauce complete with
bright orange carrots suspended in glass jars, and my favorite garnish, homemade deep fried onions.
It’s a comfort food for me, one of the few connections I have to my Vietnamese-Chinese heritage.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_lunch.BJvA_yWh_Z25Oy7S.webp&quot; alt=&quot;Lunch&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Lunch comes with a bowl of soup.
Do you drink your soup before or after the meal?
I’ve always asked this question and there’s no consistent consensus.
Usually, at restaurants, soup comes first, probably because it’s quick to make.
At home, soup usually comes after the meal, but that also changes from time to time.
I’ll let you guess what I did this time.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_soup.Crzliu3V_ZflyRh.webp&quot; alt=&quot;Soup&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Passing back to Uncle Benny’s Donuts, the allure of sugar pulls me in.
In the past, I dated a girl who’s In n Out order was always two burgers, one for the walk
home and one to eat at home.
In the present, I slip into a chair with two original glazed donuts and a Vietnamese iced coffee.
One donut to eat here, one later for the walk home.&lt;/p&gt;
&lt;p&gt;I have a soft spot for donut shops.
Throughout my travels in California, the person behind the counter is usually Asian,
probably an immigrant, selling donuts for a better life.
It reminds me of all the Chinese immigrants I grew up around.
Uncles and Aunties in a foreign land, just trying to get by.&lt;/p&gt;
&lt;p&gt;How would I describe these donuts?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s soft but it’s chewy, it’s got that Krispy Kreme crackly frosting.
Kinda cold inside which gives it a nice silky mouth feel.
It’s fluffy, cakey, not cakey like &lt;a href=&quot;https://www.toppotdoughnuts.com/pages/doughnuts&quot;&gt;Top Pot’s donuts&lt;/a&gt;,
but pretty close.
It’s good.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I say this into my Pixel’s voice recorder app.
On these walks, I’ll record thoughts as they come to mind.
Gathering notes to be reviewed, compiled, and given life in these blog posts.
I’ve considered publishing the raw sound files before.
Unfortunately, I don’t think it would make good content since I tend to ramble,
but maybe it could be good Patreon content if I ever get that far.
Google’s voice recorder does a pretty good job of transcribing my verbal notes
which helps speed up the process of organizing these thoughts as I write.&lt;/p&gt;
&lt;p&gt;The donut and sweet sweet coffee fills me with guilt.
Definitely too many calories in too small an amount of time.
A walk through the park seems like a fair sentence to atone for my caloric crimes.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_ggp.DR0-mLtP_1EDQSG.webp&quot; alt=&quot;Golden Gate Park&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Lots of streets in San Francisco were closed to vehicular traffic during the pandemic.
One of these streets is JFK Drive, a winding road that divides Golden Gate Park into two uneven halves.
Last year, San Francisco voted to permanently close this section of JFK
and allow it to remain a public walking space.
This is my first time coming down JFK since it became a walking space but I have
fond memories of COVID times and walking down other closed streets.&lt;/p&gt;
&lt;img src=&quot;/_astro/05_jfk.CV4TGtwi_Z12bvlq.webp&quot; alt=&quot;JFK Dr&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;As a climber, I found the “No Climbing” sign amusing.&lt;/p&gt;
&lt;p&gt;Onward, into Richmond, for more coffee. Never too much coffee.&lt;/p&gt;
&lt;img src=&quot;/_astro/06_library.D0Get158_ZHk7sY.webp&quot; alt=&quot;Balboa Library&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/07_books.DEYdAgkM_ZeFhBU.webp&quot; alt=&quot;Library Books&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;h1 id=&quot;balboa-richmond-district&quot;&gt;Balboa (Richmond District)&lt;/h1&gt;
&lt;p&gt;He comes down the street, recognizing the classic beige 70’s style VW bus.
It brings back fond memories to the previous year, the first time he saw that bus on his first
visit to the Coffee Movement near China Town.&lt;/p&gt;
&lt;img src=&quot;/_astro/09_coffee_bus.DfKlTBBk_Zvh7NS.webp&quot; alt=&quot;Coffee Bus&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Nearing the entrance, he sees there’s a line.
There’s always a line.
But that’s a good sign.
Good things are always worth waiting for.&lt;/p&gt;
&lt;p&gt;He waits behind a man in full black denim, jacket and trousers, ending
in black leather boots with shining silver trim.
The neo-punk hipster aesthetic is a stark contrast to the soft
pastel minimalism of the coffee shop’s decor.&lt;/p&gt;
&lt;p&gt;As he cross the threshold, the boy is suddenly and painfully aware of the Coffee Movement tote in his hand.
Foreign judgment invades his mind.
Do I look like a tool bringing the merchandise of the store to the store that
I got it from?
But just as quickly, he dismisses the thought, no need to dwell on what may or
may not be the trivial judgment of others.&lt;/p&gt;
&lt;p&gt;In the shop, on the back wall are two oak wooden shelves holding an array of neatly stacked glass cups and ceramic mugs.
In front of the shelf is a white counter with inbuilt espresso machines and milk frothers.
A line of baristas operate the equipment.
Working together, they form a conveyor belt operation.
Takes your order, pass it on.
Pulls the shot, pass it on.
Steam the milk, pass it on.
Pour the latte art, pass it on.
Takes your payment, pass it on.
Like a well caffeinated machine.&lt;/p&gt;
&lt;p&gt;Opposing the barista factory floor, are the large store windows,
letting through the afternoon sunlight.
He’s in luck, as a single open bar stool awaits at the end of the floating counter.&lt;/p&gt;
&lt;img src=&quot;/_astro/10_magazine.B793BvEX_1U9BDB.webp&quot; alt=&quot;Japanese Coffee Magazine&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;At his seat, he finds a thickly bound glossy Japanese coffee magzine [0], as if placed just for him.
From years of reading manga, he knows to start reading right to left, thumbing through [1]
the pages of minimalist cafes, hard working roasters, and pastel colored pour over cones.
This is his appetizer for the upcoming full course of reading World War II historical fiction.
Opening his novel, the din of the baristas and other guests fades away as he loses himself
in the pages of &lt;em&gt;All the Light We Cannot See&lt;/em&gt;, transported to the ocean side town of Saint Malo.&lt;/p&gt;
&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;/h2&gt;
&lt;p&gt;I hope you enjoy my try at a third person narrative style.
It’s a departure form my typical first person narration but I want to try
something akin to the style that I’m currently reading.
It’s been awhile since I’ve read fiction and I’ve been enjoying it alot.
It’s fun to read and it’s fun to try to emulate in my writing.&lt;/p&gt;
&lt;p&gt;[0] My original inspiration for writing these walking blog posts was Craig Mod’s
&lt;a href=&quot;https://craigmod.com/ridgeline/146/&quot;&gt;TOKIO TŌKYŌ TOKYO&lt;/a&gt; which was
a week long pop-up newsletter where Craig walks through Tokyo, always ending
his day at a Kissa or coffee shop or home to give away a copy of his photo book,
&lt;a href=&quot;https://shop.specialprojects.jp/products/kissa-by-kissa-4th-ed&quot;&gt;&lt;em&gt;Kissa by Kissa&lt;/em&gt;&lt;/a&gt;.
The book is in English, which he often has to apologize for as the people he gives the book
to are Japanese.
This is in contrast to this colorful coffee magazine I found at Coffee Movement.
Instead of an English book in Japan, it’s a Japanese magazine in America.
Like the photo book, the magazine is filled with photos of
coffee roasters, grinders, pour over cones, and associated stories all in unintelligible Japanese.
I found this reversal quite amusing.&lt;/p&gt;
&lt;p&gt;By the way, Craig is currently doing &lt;a href=&quot;https://craigmod.com/ridgeline/154/&quot;&gt;TOKIO TŌKYŌ TOKYO²&lt;/a&gt;
which I would highly recommend you sign up for.
There’s only a few days left for this edition of the newsletter.&lt;/p&gt;
&lt;p&gt;[1] There’s something I love about being able to page through a physical book.
I find such joy in being able to turn to a random page, to skim through the chapter
titles and get a feeling for a book before I dive in.&lt;/p&gt;
&lt;h1 id=&quot;back-to-noriega&quot;&gt;Back to Noriega&lt;/h1&gt;
&lt;p&gt;I make my way back through Golden Gate Park, taking 30th street through the Sunset,
heading back to Noriega.
On the way, I pass by Lawton Elementary School.
Groups of parents are picking up their children, asking about their day at school.
School on a Saturday?
But I don’t actually understand a word they say, it’s all Japanese.
Must be Japanese Saturday School.&lt;/p&gt;
&lt;p&gt;As a kid, I went to Chinese Saturday School.
I hated it, a weeks worth of classes crammed into a single day,
then a weeks worth of homework which of course I’d procrastinate on until the last day.
But as I got older, I started wanting more and more to reconnect with my cultural roots.
I took a community college Chinese course in High School.
In college, I studied for a summer in Taiwan.
But even after all that, unfortunately, I’m still mostly illiterate when reading Chinese.
Fluency is still a pipe dream that I keep telling myself will someday happen.
Most importantly, I want my children to be able to speak Chinese.
I don’t want my family’s culture to die in a single generation.
I want my children to know our holidays and the foods that we eat on those days.
I want my children to love the foods my parents taught me to love.&lt;/p&gt;
&lt;h1 id=&quot;叉燒&quot;&gt;叉燒&lt;/h1&gt;
&lt;p&gt;In the shop window are glistening whole roast ducks and chickens hanging from their necks on
metal hooks, heads still attached.
Racks of perfectly barbecued pork ribs with crispy blistered skin lay in the trays below.
The soy sauce sweet smell of the cha siu glaze wafts out the door.
Wooden cutting boards reverberate as the two middle aged Chinese butchers, one fat, one thin,
swing away with enormous cleavers, chopping the meat into bite-sized morsels of
crispy skin hiding soft meat interleaved with juicy fat.&lt;/p&gt;
&lt;p&gt;He gets in line.
The line is long but orderly, filled with older Chinese men and women, here
to pick the nights family dinner.
It’s Chinese New Year Eve.&lt;/p&gt;
&lt;p&gt;Finally, at the front of the line the thin butchers yells above the din in Cantonese.&lt;/p&gt;
&lt;p&gt;“What do you want?”&lt;/p&gt;
&lt;p&gt;The boy responds in Mandarin. “Do you still have BBQ Pork?”&lt;/p&gt;
&lt;p&gt;“Wait a bit”&lt;/p&gt;
&lt;p&gt;And he gets pushed down the line to a younger Chinese woman with straight dyed brown hair
who takes his order.
Looking at his young complexion and American dress style, she immediately switches to
English.&lt;/p&gt;
&lt;p&gt;“What do you want?”&lt;/p&gt;
&lt;p&gt;In a mix of English and Cantonese he says “Can I get Cha Siu?”.&lt;/p&gt;
&lt;p&gt;“You’ll have to wait a bit, cut or no cut?” she responds in English.&lt;/p&gt;
&lt;p&gt;“No cut.” this time in English.&lt;/p&gt;
&lt;p&gt;“How much?” she asks, again using English.&lt;/p&gt;
&lt;p&gt;“Two pounds” he responds in Mandarin.&lt;/p&gt;
&lt;p&gt;But she doesn’t take him seriously, already she’s moving on to the
man with thinning hair behind him, old enough to be his father.
The older man asks for the same thing, a pound of BBQ pork.
But she takes the middle aged man seriously, he gets a ticket with his waiting number.
The boy feels invisible, where was his ticket?
It’s as if the counter lady couldn’t believe that a kid like him would actually want to buy this food.
After standing for a short eternity, she finally notices him again and scrawls
quickly a number, 9, before handing it across the counter.
He steps back, making room for an older Chinese lady who again makes the same order, Cha Siu.
She gets a wait time, 10 minutes, which the boy is able to understand with his rudimentary Cantonese.
The throng of older Cantonese folk stand around the American boy.
Some are speaking in English, others are yelling in Cantonese.
There’s tension in the air, everyone is waiting for the same thing.&lt;/p&gt;
&lt;p&gt;As they wait, multiple whole suckling pigs are carried out.
An entire rack of pork ribs with shiny craggly crispy skin.
It’s Chinese New Year Eve.&lt;/p&gt;
&lt;p&gt;Finally, after 15 minutes, a man walks out of the kitchen holding several logs of meat
hanging from hooks.
The flesh is a caramel brown, fading on one end to charcoal black, and on the other side, to an ombre reddish hue.
The room fills with the aroma of sweet soy glaze.&lt;/p&gt;
&lt;p&gt;“Number One!”&lt;/p&gt;
&lt;p&gt;And Number One walks up with his ticket outstretched.
The tension rises.
The crowd surges forward but the boy hangs back, patiently biding his time,
unwilling to associate with the disorderly Chinese stereotype.
But he’s wrong, it is a crowd, but it’s orderly, each person waits their turn, for
their number to be called, and soon enough, it’s his turn.&lt;/p&gt;
&lt;p&gt;“Number Nine!”&lt;/p&gt;
&lt;p&gt;It’s his turn now to push through the crowd of older Cantonese folk,
to the glass counter top, to his trophy, the prize for the hour long wait.
It’s Chinese New Year Eve.&lt;/p&gt;
&lt;img src=&quot;/_astro/11_bbq.JF0iKizT_Z2hGxJD.webp&quot; alt=&quot;Cha Siu&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Sat, 21 Jan 2023 00:00:00 GMT</pubDate></item><item><title>Embrace NoPoo</title><link>https://enochchau.com/blog/2023/embrace-nopoo/</link><guid isPermaLink="true">https://enochchau.com/blog/2023/embrace-nopoo/</guid><description>&lt;p&gt;I stopped shampooing my hair back in September.
A lot you probably think I’m disgusting but hear me out.&lt;/p&gt;
&lt;p&gt;Look at this guy, you’ve probably seen him in a history text book before.&lt;/p&gt;
&lt;img src=&quot;https://1.bp.blogspot.com/-uMBKOQvp7KM/WJQgaqxtNTI/AAAAAAAAMOw/9WFKgBbxCJwnMMydncy82tfayoVF4PCmgCLcB/s1600/young_Stalin_1.jpg&quot; alt=&quot;young stalin&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1174&quot; height=&quot;160&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;This is a photo of Stalin in 1901.
Look at that hair, it’s damn bangin.
Shampoo was invented in 1927, I challenge you to take a look at photos of people before 1927.
They probably all have hair that looks as good as young Stalin.&lt;/p&gt;
&lt;p&gt;Anyways, I didn’t just up and give up shampooing entirely, it was a gradual process.
I was a swimmer growing up which means that I pretty much shampooed every day after practice.
The only thing worse than washing your hair is not washing your hair when there’s
pool chemicals eating away at it.
Trust me, I’m Asian, my hair is supposed to be black, but I had light brown hair for awhile
from &lt;a href=&quot;https://www.reddit.com/r/ChainsawMan/comments/z47kqc/damn_it_gun_devil/&quot;&gt;the damn pool chemical devil&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then, in college I stopped swimming and grew my hair long.
I got some tips from my mom and my girl friends and they all said not to wash
my hair everyday.
So I went from a daily wash to maybe 2-3 times a week.
I think I looked damn swell.
I kept up the 2-3 times a week even after cutting my hair short again.&lt;/p&gt;
&lt;img src=&quot;/_astro/long-hair.DM8YLG-R_11E7mD.webp&quot; alt=&quot;long hair&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1019&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Then, back in September, I just up and decided to completely stop washing my hair.
Thus, NoPoo (no shampoo).&lt;/p&gt;
&lt;p&gt;If you’re going to try this, be prepared to have greasy hair for a few weeks after starting.
The body knows how much sebum (hair oils) it needs to produce.
When I was constantly washing my hair, I naturally produced more sebum.
At the beginning of my NoPoo journey, my hair was pretty greasy but now, it feels as good as ever.&lt;/p&gt;
&lt;p&gt;And this is what I look like now.
This photo is from three days ago in &lt;a href=&quot;https://goo.gl/maps/aaMjiebgj9FFGDHBA&quot;&gt;an ice cream shop in St. George, Utah&lt;/a&gt;.
My hair feels healthy (not too greasy) and I’m able to give it some style without any product.
I will still sometimes wash my hair after a haircut or a really dirty climbing trip, but
for the most part, I’m going without the shampoo.&lt;/p&gt;
&lt;img src=&quot;/_astro/current-hair.D7lmBN6B_pQdi9.webp&quot; alt=&quot;current hair&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;625&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;If you’d like some additional pointers, I’ve heared the &lt;a href=&quot;https://www.reddit.com/r/NoPoo/&quot;&gt;NoPoo subreddit&lt;/a&gt;
is a pretty good resource, although I haven’t looked into it too much.
Johnny Harris made an &lt;a href=&quot;https://www.youtube.com/watch?v=T-_HKFjxVl0&quot;&gt;interesting video&lt;/a&gt; on this topic this as well.&lt;/p&gt;
&lt;p&gt;I realize that this probably isn’t for everyone, but I’m just here to say that it worked for me.
That’s two minutes that I save in the shower now that I can put towards writing more of these posts.&lt;/p&gt;</description><pubDate>Wed, 11 Jan 2023 00:00:00 GMT</pubDate></item><item><title>Five Months of Coffee</title><link>https://enochchau.com/blog/2022/five-months-of-coffee/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/five-months-of-coffee/</guid><description>&lt;p&gt;I like to collect things.
Maybe it’s a habit adopted from my immigrant parents.
Maybe it’s an obsession of my mathematically inclined brain to collect and analyze the minutia of my life.
Maybe it’s the artist in me believing that mundane objects could be combined into mixed medium visual art.&lt;/p&gt;
&lt;p&gt;Anyways, here’s the coffee I’ve drank in the last five months.
I don’t have the exact dates of when I bought each bag but I’m going off roast date which
should be close enough.&lt;/p&gt;
&lt;img src=&quot;/_astro/coffee_bags.CnaEjOy6_Z1RlFuM.webp&quot; alt=&quot;coffee bags&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;In the photo, the bags are sorted from left to right, top to bottom in order of oldest to newest.&lt;/p&gt;


















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Roaster&lt;/th&gt;&lt;th&gt;Tasting Notes&lt;/th&gt;&lt;th&gt;Roast Date&lt;/th&gt;&lt;th&gt;Origin&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Blueboon&lt;/td&gt;&lt;td&gt;Sight Glass, &lt;em&gt;San Francisco, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Milk Chocolate, Mandarin, Honeysuckle&lt;/td&gt;&lt;td&gt;August 26&lt;/td&gt;&lt;td&gt;blend from Central America and East Africa&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;El Pilar&lt;/td&gt;&lt;td&gt;Four Barrel Coffee, &lt;em&gt;San Francisco, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Syrup, Elderberry, Molasses&lt;/td&gt;&lt;td&gt;August 22&lt;/td&gt;&lt;td&gt;Guatemala&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Tanzania Peaberry&lt;/td&gt;&lt;td&gt;Publik, &lt;em&gt;Salt Lake City, Utah&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Green Grape, Passion Fruit, Crisp&lt;/td&gt;&lt;td&gt;September 13&lt;/td&gt;&lt;td&gt;Tanzania&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Daye Bensa&lt;/td&gt;&lt;td&gt;Mr Espresso, &lt;em&gt;Oakland, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Strawberry, Guava, Meyer Lemon&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;Ethiopia&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Las Polomas&lt;/td&gt;&lt;td&gt;Ritual, &lt;em&gt;San Francisco, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Citrus, Raspberry, Floral&lt;/td&gt;&lt;td&gt;October 9&lt;/td&gt;&lt;td&gt;Guatemala&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Samuel Junior Bermudez&lt;/td&gt;&lt;td&gt;Manhattan, &lt;em&gt;Rotterdam, The Netherlands&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Star Anise, Dark Fudge, Verbena&lt;/td&gt;&lt;td&gt;October 18&lt;/td&gt;&lt;td&gt;Colombia&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Isimbi&lt;/td&gt;&lt;td&gt;Verve, &lt;em&gt;Santa Cruz, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Golden Raisin, Hazelnut, Cacao&lt;/td&gt;&lt;td&gt;October 18&lt;/td&gt;&lt;td&gt;Rwanda&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Keynote&lt;/td&gt;&lt;td&gt;Chromatic, &lt;em&gt;San Jose, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Caramel, Fudge&lt;/td&gt;&lt;td&gt;November 21&lt;/td&gt;&lt;td&gt;blend from Brazil&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hunky Dory &lt;em&gt;(decaf)&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Sight Glass, &lt;em&gt;San Francisco, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Toffee, Red Grape, Bergamot&lt;/td&gt;&lt;td&gt;November 10&lt;/td&gt;&lt;td&gt;Varying&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lagunetas&lt;/td&gt;&lt;td&gt;Four Barrel Coffee, &lt;em&gt;San Francisco, California&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Lavender, Cane Sugar&lt;/td&gt;&lt;td&gt;December 11&lt;/td&gt;&lt;td&gt;El Salvador&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Most of the bags I get from Whole Foods.
Whole Foods generally stocks a good selection of local roasters.
If you also live in the Bay Area, I’ve found that the South Bay Whole Foods
will carry a wider selection of roasters (like Chromatic).
The freshness of the beans is pretty hit or miss but the cost per bag is
cheaper than buying it directly from the roaster, especially if they have a sale.&lt;/p&gt;
&lt;p&gt;A few of the bags I got directly from coffee shops.
Most notably, the bag from Publik is from my travels to Salt Lake City, Utah.
I visited their location on W Temple Street last time I was climbing in Little Cottonwood Canyon and Joe’s Valley.
I try to buy a bag of coffee from a local roaster whenever I travel.
Local coffee is also a &lt;a href=&quot;https://tiramisu.bearblog.dev/coffee-gift/&quot;&gt;really easy gift for your friends&lt;/a&gt;
that are into coffee.&lt;/p&gt;
&lt;p&gt;I probably spend about $20 - $40 a month on coffee.
That’s a lot considering I used to spend less than $15 a month back in College.
My &lt;a href=&quot;/blog/2022/living-with-my-parents&quot;&gt;living expenses are low&lt;/a&gt; right now so I can justify buying nicer things.
That cost doesn’t include all the trips I make to coffee shops though, since I
do love a good espresso drink, which I can’t make at home.&lt;/p&gt;
&lt;p&gt;I can’t say that’ve learned very much about tasting coffee.
I know what flavors I like (acidic) and what I don’t (chocolaty) but
beyond that, I don’t have any advice on which bags to pick.&lt;/p&gt;
&lt;p&gt;Well, now that I’ve recorded this data, I can finally throw away these bags.
See you in about five months!&lt;/p&gt;</description><pubDate>Thu, 29 Dec 2022 00:00:00 GMT</pubDate></item><item><title>Christmas (J) Tree</title><link>https://enochchau.com/blog/2022/christmas-j-tree/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/christmas-j-tree/</guid><description>&lt;p&gt;It’s Christmas and this year I spent it in Joshua Tree.
Most people take the holidays to go home and visit their parents but
my family doesn’t really celebrate Christmas.
Something to do with being Chinese Christians and Christmas not actually
being Jesus’ birthday.
Just so happens that Daniel and Kodai don’t care too much for family Christmas either.&lt;/p&gt;
&lt;p&gt;I climbed a lot in JTree in the fall of 2019 up till the early Spring of 2020.
Lots of fond memories were forged in the blazing sun and toothy quartz monzonite.
Kodai and I used to make the 2.5 hour drive from Irvine almost every other weekend to meet up with Nate,
Diya, and friends to chuff on some sandbag problem The Stonemasters FA’d 40 years ago.&lt;/p&gt;
&lt;p&gt;This time was no different, still chuffing on Nicole overhang, JBMFP, Strawberry Contraceptives.
Now I got some new chuffs to wrap up on Dream Sequence and Meadows Direct.
I finally stepped up to try White Rasta but unfortunately (fort, unfort, fortnite) fear is the mind killer.
Nevertheless, I have a new high point on JBMFP.
I finally did False up 20 after staring at it for forever and I sent Sloper Safari.
It’s kinda cool to come back after 2 years and having everything feel less sandbag than before.&lt;/p&gt;
&lt;p&gt;Special thanks to DMX for giving us the song &lt;a href=&quot;https://www.youtube.com/watch?v=fGx6K90TmCI&quot;&gt;“X Gon’ Give It To Ya”&lt;/a&gt;
which always inspires me to try extra hard.
And shout-out to Tamale guy for being there for us every morning.
And an extra special thanks to Jason for making the drive out on our last day.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_stacked_pads.BBIQBTDe_Z1NjV6T.webp&quot; alt=&quot;susing out meadows direct&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Merry Christmas Y’all&lt;/p&gt;
&lt;img src=&quot;/_astro/01_merry_christmas.CWSEwaKL_6uUuq.webp&quot; alt=&quot;merry christmas&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;P.S. DMX says, you can do it!&lt;/p&gt;</description><pubDate>Tue, 27 Dec 2022 00:00:00 GMT</pubDate></item><item><title>Using Neovim as a Fennel Compiler</title><link>https://enochchau.com/blog/2022/using-neovim-as-a-fennel-compiler/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/using-neovim-as-a-fennel-compiler/</guid><description>&lt;p&gt;I’ve been doing &lt;a href=&quot;https://adventofcode.com/2022&quot;&gt;Advent of Code&lt;/a&gt; this year in &lt;a href=&quot;https://fennel-lang.org&quot;&gt;Fennel&lt;/a&gt;.
(I’m updating my answers every day in &lt;a href=&quot;https://github.com/enochchau/adventofcode2022&quot;&gt;a GitHub repo&lt;/a&gt;.)
I use a few different computers day to day and I need to setup each computer with
the proper Lua libraries and Fennel compiler.
For speed, I want to run the Fennel compiler under LuaJIT and I also want to have
access to the &lt;a href=&quot;https://github.com/kikito/inspect.lua&quot;&gt;&lt;code&gt;inspect.lua&lt;/code&gt;&lt;/a&gt; library for debugging.
I got everything working alright on MacOS but on Linux, Fennel wasn’t detecting
luarocks libraries.
That’s when I realized I don’t actually need an extra program to compile and run Fennel.
I already have everything I need built directly into Neovim.&lt;/p&gt;
&lt;p&gt;Running Fennel scripts through Neovim gives me access to all of Neovim’s builtin APIs.
This is a fantastic environment for debugging and development since a lot of
helper functions already exist. For example &lt;code&gt;vim.inspect&lt;/code&gt; implements the same
functionality as &lt;code&gt;inspect.lua&lt;/code&gt; and Neovim even provides a short hand for
printing &lt;code&gt;vim.inspect&lt;/code&gt;’s results using &lt;code&gt;vim.pretty_print&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I already partly setup Neovim as a Fennel compiler in a &lt;a href=&quot;/blog/2022/lispy-neovim&quot;&gt;previous blog post&lt;/a&gt;.
The implementation I originally came up with worked well for compiling a file to output
but what if I want to write that output to stdout instead?
Naively, I initially thought I could just use the &lt;code&gt;print()&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;FnlCompile&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; debug.traceback&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ~= &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fennel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.traceback &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        debug&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.traceback = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fennel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.traceback&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;unpack&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.fn.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.args, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;missing input path&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; stream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;open_stream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fnl_compile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;stream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; out_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; ~= &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;nil&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;assert&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;io.open&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;        file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;close&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        -- if no file path is provided lets just print the contents&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;nargs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; })&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;margin: 1rem&quot;&gt;&lt;/div&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;FENNEL_COMPILE&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; nvim&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --headless&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;FnlCompile test.fnl&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; +q&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But then I ran into an issue: &lt;code&gt;print&lt;/code&gt; doesn’t print to stdout when this command is run.
No matter how I redirected the output, I couldn’t get
the it to print to stdout.
A quick search, helped me find this &lt;a href=&quot;https://github.com/neovim/neovim/issues/4772#issuecomment-219962823&quot;&gt;GitHub issue&lt;/a&gt;.
I found that, you have to explicitly write your output to &lt;code&gt;/dev/stdout&lt;/code&gt;.
My fix was to create a helper function for printing to stdout.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; print_stdout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    message&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.fn.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.fn.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;writefile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/dev/stdout&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can print the results of compilation directly to stdout.&lt;/p&gt;
&lt;p&gt;I also need to setup a way to run individual Fennel scripts in the Neovim environment.
The Fennel compiler API includes
&lt;a href=&quot;https://fennel-lang.org/api#evaluate-a-file-of-fennel&quot;&gt;a function for evaluation of Fennel files&lt;/a&gt;.
I can expose this function using a simple Neovim command.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;FnlRun&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;unpack&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.fn.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.args, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;missing input path&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fennel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;dofile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;    -- print an extra newline to seperate script output from from the printed return values&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    print_stdout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;inspect&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;nargs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; })&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can run a Fennel script using:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;FENNEL_COMPILE&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; nvim&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --headless&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;FnlRun test.fnl&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; +q&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This provides a very nice DX.
My goal for using Fennel in Advent of Code is to practice writing future Neovim plugins.
I no longer have to setup Fennel, Lua, or luarocks.
I have my entire compile and run cycle going through Neovim which I already
have installed on all my systems.&lt;/p&gt;</description><pubDate>Sat, 03 Dec 2022 00:00:00 GMT</pubDate></item><item><title>Decaf</title><link>https://enochchau.com/blog/2022/decaf/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/decaf/</guid><description>&lt;p&gt;I started drinking decaf coffee and it’s kinda nice.
Sometimes I just want to have a cup of coffee at 1AM before going to sleep.
I’ve been able to separate my minds association between caffeine the drug
and coffee the drink.
Usually, I drink coffee in the morning because I want a quick energy boost.
Now I can drink coffee at night just to enjoy the flavor.
I can also drink way more coffee per day which I will happily indulge in.&lt;/p&gt;
&lt;img src=&quot;/_astro/decaf.Dhm57Z-a_Zs1afL.webp&quot; alt=&quot;Sight glass decaf&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; </description><pubDate>Sat, 03 Dec 2022 00:00:00 GMT</pubDate></item><item><title>Researching Lispy Neovim</title><link>https://enochchau.com/blog/2022/lispy-neovim/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/lispy-neovim/</guid><description>&lt;p&gt;Since around Neovim 0.5, Lua was added as an alternative configuration language
to Vimscript.
Vimscript isn’t bad but I think anyone who has written Vimscript will tell you that
Lua is an easier language to learn.
&lt;a href=&quot;https://fennel-lang.org/&quot;&gt;Fennel&lt;/a&gt; is a Lisp that compiles to Lua which
means you can also use it in Neovim (sort of).
Every now and then, there’s a post that comes up on &lt;a href=&quot;https://news.ycombinator.com/&quot;&gt;hn&lt;/a&gt;
about Lisp. These posts were my original inspiration for learning Fennel.
I’ve dabbled a bit with Fennel before and I’m back at it again.
Specifically, I want to create my own Fennel adapter plugin for Neovim.&lt;/p&gt;
&lt;p&gt;There’s currently three implementations of Fennel for Neovim that I know of.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Olical/aniseed&quot;&gt;Olical/aniseed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rktjmp/hotpot.nvim&quot;&gt;rktjmp/hotpot.nvim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/udayvir-singh/tangerine.nvim&quot;&gt;udayvir-singh/tangerine.nvim&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these plugins basically do the same thing.
They add Fennel as a first class language to Neovim which includes features such
as writing config files in Fennel, a built in repl, and automatic compilation.&lt;/p&gt;
&lt;p&gt;I’ve been exploring the source code for Hotpot and Tangerine.
Both plugins use different implementations to achieve more or less the same feature set.
Mostly, I’m interested in finding strategies for automatic compilation of Fennel
to Lua.&lt;/p&gt;
&lt;h1 id=&quot;hotpot&quot;&gt;Hotpot&lt;/h1&gt;
&lt;p&gt;Hotpot comes pretty close to how the Fennel docs recommend implementing Fennel support.
Fennel provides some &lt;a href=&quot;https://fennel-lang.org/api#use-luas-built-in-require-function&quot;&gt;builtin functions&lt;/a&gt;
that allow you to automatically source Fennel files.
It does this by adding a package loader specifically for Fennel files.&lt;/p&gt;
&lt;p&gt;When you &lt;code&gt;require&lt;/code&gt; a module in Lua, the resolution algorithm uses package loaders
to look for the file you want.
There’s &lt;a href=&quot;https://github.com/lewis6991/impatient.nvim/blob/d3dd30ff0b811756e735eb9020609fa315bfbbcc/lua/impatient.lua#L424-L429&quot;&gt;5 of them&lt;/a&gt;
that are built-in to Neovim:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;package.preload&lt;/code&gt; - predefined package locations stored in a table&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vim._load_package&lt;/code&gt; - Neovim’s run-time path&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.path&lt;/code&gt; - default paths to look through for Lua modules&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.cpath&lt;/code&gt; - default paths to look through for C modules&lt;/li&gt;
&lt;li&gt;&lt;code&gt;all-in-one&lt;/code&gt; - tries looking everywhere since everything else failed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What Hotpot (and Fennel) do is they add a new loader that can resolve Fennel files.
For Hotpot, this loader is &lt;a href=&quot;https://github.com/rktjmp/hotpot.nvim/blob/562e3e0e04c092c43a9cddffae4c9a748afb186f/fnl/hotpot.fnl#L17&quot;&gt;injected as the first loader&lt;/a&gt;
which means that it gives the highest priority to resolving Fennel files over Lua files
whenever &lt;code&gt;require&lt;/code&gt; is called.&lt;/p&gt;
&lt;p&gt;In order to speed up startup time, Hotpot implements a cache.
This cache includes 2 parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compiled Lua files&lt;/li&gt;
&lt;li&gt;The cache index which keeps track of when these Lua files should be updated&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The cache index is basically a map of module names to compiled file location.
When a module is required, Hotpot will check the index to see if the module
has already been compiled.
Hotpot then diffs the write time of the original Fennel file against it’s compiled
Lua counterpart to see if it should recompile the Fennel file.&lt;/p&gt;
&lt;p&gt;The cache index is a bytecode file that is encoded using Neovim’s builtin
&lt;a href=&quot;https://neovim.io/doc/user/lua.html#lua-mpack&quot;&gt;message pack functionality&lt;/a&gt;.
This means that it can be loaded very quickly.&lt;/p&gt;
&lt;p&gt;So to summarize, Hotpot injects a package loader that can resolve Fennel files.
This package loader implements a caching system that can automatically
recompile Fennel to Lua anytime the source Fennel code has changed.&lt;/p&gt;
&lt;p&gt;The main difference between Hotpot and Fennel’s default package loader implementation
is that Fennel doesn’t implement this caching functionality.
For Hotpot, having a cache means that Fennel files aren’t recompiled on every startup unless
absolutely necessary, thereby improving overall startup time.&lt;/p&gt;
&lt;h1 id=&quot;tangerine&quot;&gt;Tangerine&lt;/h1&gt;
&lt;p&gt;In comparison, Tangerine has a more simple approach to keeping compiled Lua
files up to date.
Tangerine implements a series of &lt;a href=&quot;https://github.com/udayvir-singh/tangerine.nvim/blob/e23caf681bb2f7fc6d409c2228678f2d4b5a8910/fnl/tangerine/vim/hooks.fnl&quot;&gt;hooks&lt;/a&gt;
into the Neovim editing life cycle.
The hooks attach to the &lt;code&gt;VimEnter&lt;/code&gt; and &lt;code&gt;BufWritePost&lt;/code&gt; events.
These events will trigger an autocommand that diffs all Fennel source files against
their compiled Lua counterparts and recompiles the ones that are stale.
Every Fennel source file’s timestamp is checked whenever the hook is run.&lt;/p&gt;
&lt;p&gt;On start, Tangerine injects the path that it uses for compiled Lua files
into the &lt;code&gt;package.path&lt;/code&gt;.
When using &lt;code&gt;require&lt;/code&gt; to load a module, Lua will also look through Tangerine’s injected paths to resolve the module.&lt;/p&gt;
&lt;p&gt;To summarize, Tangerine doesn’t have a fancy caching system.
It just diffs and re-compiles files whenever they’re edited in Neovim.
This works pretty well since every file is checked whenever you start Neovim.&lt;/p&gt;
&lt;h1 id=&quot;brainstorming&quot;&gt;Brainstorming&lt;/h1&gt;
&lt;p&gt;Both of these implementations use interesting tactics to keep Neovim’s startup fast
and compiled Lua files up to date.
The thing I don’t like about both of them though is that there’s always some
startup time associated with checking the validity of compiled Lua files.
Currently, I’m opting to use a &lt;a href=&quot;https://github.com/enochchau/dotfiles/blob/52a20c56831084fa4480198ededea3122a567e34/roles/nvim/files/Makefile&quot;&gt;Makefile&lt;/a&gt;
that I have to manually run after editing Fennel files.
While I don’t get the seamless experience of:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;edit -&amp;gt; restart editor -&amp;gt; see changes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when I edit my Neovim configs, I think it’s a small price to pay to save a few
milliseconds on every startup where I’m not editing my configs.&lt;/p&gt;
&lt;p&gt;One fun thing I was able to do was to use Neovim itself as a Fennel compiler.
I added this line at the &lt;a href=&quot;https://github.com/enochchau/dotfiles/blob/52a20c56831084fa4480198ededea3122a567e34/roles/nvim/files/init.lua#L3-L6&quot;&gt;top of my &lt;code&gt;init.lua&lt;/code&gt;&lt;/a&gt;
that checks if I’m running Neovim in Fennel compiler mode.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.env[&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;FENNEL_COMPILE&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    require&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;bulb&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;bulb&lt;/code&gt; module loads the &lt;code&gt;fennel.lua&lt;/code&gt; compiler API.
I only load &lt;code&gt;bulb&lt;/code&gt; and &lt;code&gt;fennel.lua&lt;/code&gt; when Neovim is in Fennel compiler mode
so that I don’t slow down my startup times when I’m using Neovim normally.
&lt;code&gt;bulb&lt;/code&gt; provides a user command called &lt;code&gt;FnlCompile&lt;/code&gt; that compiles Fennel files.
It’s a simple function that just loads a file as a byte stream, compiles it,
then writes the output.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.api.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;nvim_create_user_command&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;FnlCompile&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;unpack&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.fn.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.args, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;missing input path&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;missing output path&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; stream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;open_stream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;in_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fennel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;compileStream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;stream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, { [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;compiler-env&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] = &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;_G&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;assert&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;io.open&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;out&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;close&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;nargs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; })&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, I can run Neovim in headless mode to compile Fennel files.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;FENNEL_COMPILE&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; nvim&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --headless&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;#39;FnlCompile fnl/enoch/helpers.fnl lua/enoch/helpers.lua&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; +q&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s two main benefits of running the Fennel compiler through Neovim.
First, it allows my configs to be fully portable.
I don’t have to rely on the host computer having an installed copy of Fennel.
In addition, the compiler is always run through Neovim’s builtin LuaJIT which is
much faster than the standard Lua 5.1 implementation.&lt;/p&gt;
&lt;p&gt;Second, and more importantly, it allows me to use Neovim builtin functions in macros.
Since Fennel version 1.0, the compiler is sandboxed by default meaning that
builtin functions such as &lt;code&gt;io&lt;/code&gt;, &lt;code&gt;os&lt;/code&gt;, or any Neovim provided functions such as &lt;code&gt;vim.api&lt;/code&gt; can’t be used in macros.
But I couldn’t turn down the sweet sweet power of &lt;a href=&quot;https://github.com/enochchau/dotfiles/blob/52a20c56831084fa4480198ededea3122a567e34/roles/nvim/files/fnl/enoch/quotes.fnl#L1-L23&quot;&gt;compile time madness&lt;/a&gt;
so I just turned off compiler sandboxing.
This isn’t very secure, but I’m the only person who will be editing my configs
so I’m not too concerned.&lt;/p&gt;
&lt;p&gt;I’m not sure where I’ll go next with my Fennel adventures.
I’ll probably start on implementing a repl of sorts that’s similar to how
Vimscript and Lua can be executed in Neovim’s command line.
Ideally, I want to package up my Fennel compilation implementation into a Neovim plugin
so perhaps I will eventually ditch the Makefile.
It was really interesting learning about Lua’s module resolution algorithm
and how hackable it is.&lt;/p&gt;</description><pubDate>Fri, 25 Nov 2022 00:00:00 GMT</pubDate></item><item><title>Kuwahara Dithering</title><link>https://enochchau.com/blog/2022/kuwahara-dithering/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/kuwahara-dithering/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/kuw_dither&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;inspiration&quot;&gt;Inspiration&lt;/h1&gt;
&lt;p&gt;I’ve been interested in dithering ever since I came across
&lt;a href=&quot;https://solar.lowtechmagazine.com/2018/09/how-to-build-a-lowtech-website.html&quot;&gt;an article in Low Tech Magazine&lt;/a&gt;
on how they made their website more energy efficient.
In order to reduce network payload size when serving images, they dither all their images
which reduces file size while preserving some level of image quality.&lt;/p&gt;
&lt;p&gt;More recently, I watched a video called &lt;a href=&quot;https://youtu.be/LDhN-JK3U9g?list=LL&quot;&gt;‘The Kuwahara Filter’&lt;/a&gt;
by &lt;a href=&quot;https://www.youtube.com/@Acerola_t&quot;&gt;Acerola&lt;/a&gt;.
In the video, he explains the history of the Kuwahara filter and its potential use cases.
One &lt;a href=&quot;https://youtu.be/LDhN-JK3U9g?list=LL&amp;t=843&quot;&gt;interesting combination&lt;/a&gt;
he mentions is using the Kuwahara filter and then dithering.&lt;/p&gt;
&lt;p&gt;This got my mind churning a bit.
The combination of dithering with the Kuwahara filter sounded like it could make exciting image filters.&lt;/p&gt;
&lt;p&gt;With dithering popping back into my mind space, I remembered an old 4Chan /wg
thread with interesting edits.
The &lt;a href=&quot;https://boards.4chan.org/wg/thread/7743043&quot;&gt;link to the original thread&lt;/a&gt;
is now dead, but the original post was:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I was fucking around and found a fun way of livening up old and/or low res wallpapers, and I&amp;#39;ll be dumping some and sharing the technique. To my knowledge this will require photoshop.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;open your image in PS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;shrink it to exactly half or a quarter of your screen&amp;#39;s resolution&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;gaussian blur if desired&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;save for web as PNG-8 using &amp;quot;Restrictive&amp;quot; color reduction and &amp;quot;Pattern&amp;quot; dithering, setting colors to &amp;quot;Auto&amp;quot; or your desired bit depth&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;close, and open your newly exported file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;upscale the image to full res using &amp;quot;Nearest Neighbor&amp;quot; in resampling&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;win&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I got the following images from that original 4Chan thread.&lt;/p&gt;
&lt;img src=&quot;/_astro/4chan_lake_mtn.B7Pz4fN1_Z18J0Lw.webp&quot; alt=&quot;4chan lake mountain&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2560&quot; height=&quot;1440&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/4chan_japanese_street.mqhG36ll_1sfDgg.webp&quot; alt=&quot;4chan japanese street&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;3000&quot; height=&quot;1996&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;With the 4Chan dithering recipe, I could combine it with the Kuwahara filter to get some interesting results.
The 4Chan recipe has instructions for PhotoShop but I have non-existent PhotoShop skills.
I opted to use &lt;a href=&quot;https://imagemagick.org/&quot;&gt;ImageMagick&lt;/a&gt; instead.
ImageMagick is a program that can easily do a whole myraid of image transformations
from the command line.&lt;/p&gt;
&lt;h1 id=&quot;experimentation&quot;&gt;Experimentation&lt;/h1&gt;
&lt;p&gt;After going through all the above inspiration, I started off experimenting
with different levels of Kuwahara filtering with different dithering algorithms.
For dithering, the outputs of the Floyd-Steinberg and Riemersma filters produced interesting images
but they weren’t as geometric as the original 4Chan inspiration.
Finally, I figured out that using ordered dithering, I could achieve what I was looking for.
After some experimentation, I settled on using ordered dithering with an ordered 8x8 threshold map at level 3.&lt;/p&gt;
&lt;h1 id=&quot;results&quot;&gt;Results&lt;/h1&gt;
&lt;p&gt;The final ImageMagick script is:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;convert&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; $INPUT&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    \(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -resize&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; 50%&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; )&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;\ \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    \(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -kuwahara&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \)&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    \(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -ordered-dither&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; o8x8,3&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -colors&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 8&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \)&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    \(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -interpolate&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; Nearest&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -filter&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; Box&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \)&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;    \(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -resize&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; 200%&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \)&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    $OUTPUT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find this script in the &lt;a href=&quot;https://github.com/enochchau/kuw_dither&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To summarize:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Downscale the image&lt;/li&gt;
&lt;li&gt;Run the Kuwahara filter&lt;/li&gt;
&lt;li&gt;Use ordered dithering with 8 colors&lt;/li&gt;
&lt;li&gt;Interpolate pixels using the nearest neighbor algorithm with a Box filter&lt;/li&gt;
&lt;li&gt;Upscale back to the original image size&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here’s an example of the filter on the image that I’m currently using as my desktop wallpaper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Original&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/ocean_waves_original.Bw7L5zCu_ncWdn.webp&quot; alt=&quot;ocean waves original&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2560&quot; height=&quot;1440&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;Edited&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/ocean_waves_kuw_dither.FeU-rjSR_15HoAu.webp&quot; alt=&quot;ocean waves kuwahara dither&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2560&quot; height=&quot;1440&quot; class=&quot;image&quot;&gt; 
&lt;h2 id=&quot;tweaking-parameters&quot;&gt;Tweaking Parameters&lt;/h2&gt;
&lt;p&gt;From my observations, each parameter affects the output in a different way.&lt;/p&gt;
&lt;h3 id=&quot;downscale&quot;&gt;Downscale&lt;/h3&gt;
&lt;p&gt;Tweaking the initial downscale will result in a more obvious filter.
When the Kuwahara filter and dithering effect have fewer pixels to work with,
their effects are more pronounced.&lt;/p&gt;
&lt;h3 id=&quot;kuwahara-filter&quot;&gt;Kuwahara Filter&lt;/h3&gt;
&lt;p&gt;Increasing the Kuwahara filter radius will give a more painted like look to the final image.
More areas of color will get splotched together.&lt;/p&gt;
&lt;h3 id=&quot;dithering&quot;&gt;Dithering&lt;/h3&gt;
&lt;p&gt;Different dithering algorithms will produce different effects.
Using the Floyd-Steinberg algorithm will produce a much less pixelated look.
With Ordered dithering, I was able to produce a much more grid like pixelated look.&lt;/p&gt;
&lt;p&gt;Having 8 colors usually produces an image that looks distinctly filtered.
With 16 or 32 colors, I usually will get enough colors that output will still look
fairly close to the input in terms of color.&lt;/p&gt;
&lt;h3 id=&quot;interpolation&quot;&gt;Interpolation&lt;/h3&gt;
&lt;p&gt;Interpolation will further pixelate the final result.
Using the Box or Point filters gives a harsher look while the Triangle filter results in a softer image.&lt;/p&gt;
&lt;h3 id=&quot;upscale&quot;&gt;Upscale&lt;/h3&gt;
&lt;p&gt;The final upscale is just to get the image back to it’s original size.
I use the inverse of the downscale percentage to do this.&lt;/p&gt;</description><pubDate>Thu, 24 Nov 2022 00:00:00 GMT</pubDate></item><item><title>Self Inflicted Haircut</title><link>https://enochchau.com/blog/2022/self-inflicted-haircut/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/self-inflicted-haircut/</guid><description>&lt;p&gt;I cut my own hair yesterday.
I’ve been cutting my own hair since 2020.
If you’ve seen me in the last two years, then you’ve probably seen my self inflicted haircut.&lt;/p&gt;
&lt;p&gt;I like being self sufficient.
It’s nice not having to rely on another human for sometime so basic.&lt;/p&gt;
&lt;p&gt;Last weekend, Elliott and Tim cooked some steaks for us.
They’ve both been cooking steaks since college.
I thought the steaks were really good but the chefs kept saying it could be better.
I think that’s it’s good that they continue to strive for perfection but sometimes
it can also be good to just look back and see the progress they’ve made so far.&lt;/p&gt;
&lt;p&gt;I’d say the same thing about my hair cutting skills.
The first few I did were, thankfully, during the pandemic because whoo boy did they look bad.
But they’ve slowly gotten better and I think other people can see that too.
Sometimes, when I learn something new, I get caught up in how slow and invisible progress is.
So it’s nice that for hair cutting, my skill progression is blatantly visible on the top of my head.&lt;/p&gt;</description><pubDate>Thu, 10 Nov 2022 00:00:00 GMT</pubDate></item><item><title>Living with My Parents</title><link>https://enochchau.com/blog/2022/living-with-my-parents/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/living-with-my-parents/</guid><description>&lt;p&gt;I was finishing up my last quarter at UC Irvine when the pandemic hit in March of 2020.
I came back to campus after spring break, but many other students (rightfully) decided not to return,
including my roommates.
For two months, I lived alone in my apartment.
That was some of the loneliest two months of my life.
I was confined in a 1000 sq ft box, slowly losing my mind.
I could go for days without saying a word or hearing a human voice.
Eventually, my roommates and I eventually agreed to break our lease.&lt;/p&gt;
&lt;p&gt;I moved home, back in with my parents.
I was hoping to avoid this situation entirely, but sometimes a global
pandemic comes along and ruins all your plans.
It’s now 2022 and I’m still here.&lt;/p&gt;
&lt;p&gt;I didn’t have the best relationship with my parents growing up.
There was a fair amount of rebellion and disagreement throughout my time as a juvenile.
I’m just going to leave it at that.
But living at home now has helped to slowly mend our relationship.&lt;/p&gt;
&lt;p&gt;I see my parents everyday, I talk to them everyday.
In the last year, I’ve grown pretty close with my dad.
With my mother in Taiwan, it was &lt;a href=&quot;https://youtu.be/v8oqbWrP1QY?t=39&quot;&gt;just the two of us&lt;/a&gt; at home last year.&lt;/p&gt;
&lt;p&gt;I like talking to my parents.
Eastern culture usually dictates an authoritative relationship between parent and child,
but now that I’m grown and make my own cash, that’s mostly disappeared.
Our relationship is closer to being friends now.&lt;/p&gt;
&lt;p&gt;I still depend on my parents.
I don’t pay rent.
&lt;a href=&quot;https://www.youtube.com/shorts/LWVTsHNQIiU&quot;&gt;I drive my moms car&lt;/a&gt;.
I eat the food they cook.
My dependencies are all simple things that any people cohabiting would likely have as well.&lt;/p&gt;
&lt;p&gt;It’s not that I don’t have the means to move out.
The thought of moving out and exploring life my 20’s has been consistently sitting in the back of
my mind for the last year or so.
But I also know that moving out is an inevitable eventuality and that I should treasure
the time I have left with mom and dad.&lt;/p&gt;
&lt;p&gt;I read &lt;a href=&quot;https://twitter.com/sahilbloom/status/1571137341997318147&quot;&gt;an interesting tweet&lt;/a&gt; from hn
on the time left we have with our parents.
In summary, the average lifespan is ~80 years.
If you only see your parents once a year, you’ll only see them ‘x’ (for me this is 20) more times
before they die.
So I’m not in a huge hurry to leave.
I want to treasure the time I spend with them now, and hopefully, I can look back fondly later
in life knowing I was able to mend our relationship before their deaths.&lt;/p&gt;</description><pubDate>Mon, 07 Nov 2022 00:00:00 GMT</pubDate></item><item><title>My Creative Mediums</title><link>https://enochchau.com/blog/2022/my-creative-mediums/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/my-creative-mediums/</guid><description>&lt;p&gt;I’ve tried a few different mediums now:
&lt;a href=&quot;/create/#tunes&quot;&gt;music&lt;/a&gt;,
&lt;a href=&quot;https://www.youtube.com/channel/UC8WzolokXeJKN2d4dMrjPlA&quot;&gt;film&lt;/a&gt;,
&lt;a href=&quot;https://www.instagram.com/uhhuhhuny/&quot;&gt;photography&lt;/a&gt;,
&lt;a href=&quot;/blog&quot;&gt;writing&lt;/a&gt;, and
&lt;a href=&quot;/create&quot;&gt;painting&lt;/a&gt; to name a few.
Each medium has a number of rules that need to be understood.
You can’t break the rules until you know what they are.
Now-a-days, I gravitate towards the mediums that are easiest to be immersed in: writing and photography.
These are mediums I experience on the day to day.&lt;/p&gt;
&lt;p&gt;With writing, I read every day, mostly articles now but books from time to time.
I write, right here, on this blog.
Blogging is how I practice the grammar and vocabulary I learned in my 12 grades of government bestowed schooling.
I’ll be the first to admit that my first few blog posts are bad.
They don’t read well, but I think I’m getting better (I hope you think so too).&lt;/p&gt;
&lt;p&gt;With photography, I see pictures everyday.
On Instagram, Reddit, etc… the internet is full of images.
I take note of the things I think look good and try to emulate them.&lt;/p&gt;
&lt;p&gt;Nothing in art is new.
Everything is just a copy, an emulation, a spin, or a combination of older styles.
But you need to know how those original styles were created to understand
the process of how to break the rules.
Creativity is breaking the rules in the most aesthetic ways possible.&lt;/p&gt;
&lt;p&gt;I tend to trend away from mediums that are heavy on rules.
Music has it’s theory.
Visual art emulates lighting from the physical world.
Film is just an amalgamation of writing, cinematography, visual arts, sound design, and editing, each with it’s
own rules.
There’s a lot to learn for these mediums.
I’d like to put in the work one of these days but I haven’t been hooked enough to dive in head first.&lt;/p&gt;
&lt;p&gt;From time to time, I dabble with prose’s close cousin, poetry, but I’ve never
been able to dedicate wholeheartedly because I don’t have a good understanding of
the rules of poetry.
I have a nagging feeling that I won’t be able to make anything worthwhile if I’m
not constantly reading poetry.&lt;/p&gt;
&lt;p&gt;I used to love to draw.
I kept a sketch book with me at all times.
I drew my feelings, wrote my deepest thoughts in that book, ate oranges on it, and rolled joints on it.
There’s something lovely about the physicality of pulling an ink pen across ivory white pages.
Unfortunately, it now sits on my shelf, a melancholic reminder of a more romantic time.
Perhaps I’ll take it up again someday, but for now, I’ve replaced that physicality with a new one:
typing out these blog posts on my mechanical keyboard in my bespoke
&lt;a href=&quot;https://github.com/enochchau/dotfiles/tree/master/roles/nvim/files&quot;&gt;Vim setup&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I digress, I still want to discuss creative adjacent mediums I’ve dabbled with.&lt;/p&gt;
&lt;p&gt;I’ve thought about lumping in code as a creative medium.
I think software can have creativity associated with it.
There’s something elegant about the implementation of &lt;a href=&quot;https://www.sqlite.org/index.html&quot;&gt;SQLite&lt;/a&gt;
or &lt;a href=&quot;https://esbuild.github.io/&quot;&gt;esbuild&lt;/a&gt;.
But software probably has the most rules of any of the other mediums we’ve discussed.
Code can’t really be Jazz unless you want to solve a riddle reading your code every time you come
back to your project.&lt;/p&gt;
&lt;p&gt;Baking on the other hand, can have a little more freedom.
Baking is often described as being similar to chemistry.
There are measured ingredients that are put together in a specific order to achieve the result.
Nevertheless, you can always have some fun with your ingredients.
One of my favorite things to do is to replace vanilla with any other liquor.
Whiskey is pretty good.
I’ve tried tequila too, but I wouldn’t recommend it unless you really like tequila.
Cooking kinda goes into this column too.
Putting a dash of spice and a &lt;a href=&quot;https://www.instagram.com/nusr_et/&quot;&gt;sprinkle of salt&lt;/a&gt;
is about as close a chef can get to painting like Jackson Pollock.
Cooking can’t be as messy as modern art since whatever you end up making still has to taste palatable.&lt;/p&gt;
&lt;p&gt;With all that said, I’d say my creativity stems from whatever it is I surround myself with.
I glean ideas from conversations with my friends.
When I walk around by myself, I develop my thoughts into creative revelations.
And last but not least, I was raised by the internet, and it continues to inspire me.&lt;/p&gt;
&lt;p&gt;I hope this inspires you to makes something.&lt;/p&gt;</description><pubDate>Wed, 02 Nov 2022 00:00:00 GMT</pubDate></item><item><title>I Love Bread (Backhaus)</title><link>https://enochchau.com/blog/2022/i-love-bread/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/i-love-bread/</guid><description>&lt;p&gt;I love bread.
I love bakeries.&lt;/p&gt;
&lt;p&gt;I try to check out any local bakeries whenever I travel, but
we also have bakeries at home.
Of the ones at home, Backhaus is a lovely shop in San Mateo.
When I think of food, I usually think of things in San Francisco,
but there’s a host of great shops on the Peninsula.
Gotta rep where I’m from.&lt;/p&gt;
&lt;iframe style=&quot;border-radius:12px&quot; src=&quot;https://open.spotify.com/embed/track/2xYUDc31L21yrCkJUcaXKw?utm_source=generator&quot; width=&quot;100%&quot; height=&quot;152&quot; frameBorder=&quot;0&quot; allow=&quot;autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture&quot; loading=&quot;lazy&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;backhaus&quot;&gt;&lt;a href=&quot;https://www.backhausbread.com/&quot;&gt;Backhaus&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Backhaus has both delicious bread and delicious coffee.&lt;/p&gt;
&lt;p&gt;I’ve heard that the coffee is from &lt;a href=&quot;https://ritualcoffee.com/&quot;&gt;Ritual&lt;/a&gt;
and that the baristas at Backhaus are trained by Ritual.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=MKWyhg7SggY&quot;&gt;They sell bread, they sell loafs.&lt;/a&gt;
The asiago black pepper loaf is real good.
They also have regular loafs, sourdough loafs, and more.&lt;/p&gt;
&lt;img src=&quot;/_astro/backhaus-bread.c4nB8bk3_Zm2XjL.webp&quot; alt=&quot;backhaus bread&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The crust is crispy and nutty.
The innards are hella qq.
Just look at that gluten development.&lt;/p&gt;
&lt;img src=&quot;/_astro/backhaus-seeded-sourdough.DIohtCIO_Z1TvJcg.webp&quot; alt=&quot;backhaus seeded sourdough&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;They were serving pretzels and a beer during Oktoberfest.
It’s quite fun to get drunk in a place that I wouldn’t normally be able to get drunk.
Also, I’m super light weight so I can get drunk off one beer.&lt;/p&gt;
&lt;img src=&quot;/_astro/backhaus-pretzel.BrQWfp5s_Zkvje1.webp&quot; alt=&quot;backhaus pretzel&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;There are also baked goods on certain days.
I went to get the croissants after an early morning gym session.
One of my favorite things to do is find a cute cafe or bakery to chill at in the morning.
I usually spend my mornings around China Town so it’s nice to have a change of pace.&lt;/p&gt;
&lt;img src=&quot;/_astro/backhaus-chocolate-croissant.CTIegH5p_ZVN7uz.webp&quot; alt=&quot;backhaus chocolate croissant&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The ham and cheese croissant is pretty good too.
I’m thinking of starting a ham and cheese croissant tier list soon.
I’ve had a really good ham, cheese and jalapeño croissant from
&lt;a href=&quot;https://tartinebakery.com/&quot;&gt;Tartine&lt;/a&gt; and &lt;a href=&quot;https://nychau.com&quot;&gt;Nate&lt;/a&gt; says the ones
from &lt;a href=&quot;https://arsicault-bakery.com/locations&quot;&gt;Arsicult&lt;/a&gt; are real good too.&lt;/p&gt;
&lt;img src=&quot;/_astro/backhaus-ham-and-cheese-croissant.Cjd9zjDv_Z2qM83w.webp&quot; alt=&quot;backhaus ham and cheese croissant&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I had a dream in college that if the electrical engineering thing didn’t work out,
I’d open a bakery called ‘Half Baked’.
At ‘Half Baked’, half the shop would sell delicious baked goods and the other
half would sell the same baked goods but with weed in them.
Well, the engineering thing didn’t work out but I ended up getting a regular
desk job anyways.&lt;/p&gt;</description><pubDate>Wed, 26 Oct 2022 00:00:00 GMT</pubDate></item><item><title>Language: Bridges and Walls</title><link>https://enochchau.com/blog/2022/language-bridges-and-walls/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/language-bridges-and-walls/</guid><description>&lt;p&gt;Language rules us all, language brings us together, and language can bind us.
Words are what we use to communicate and relate to one another.
Language is the basis of culture.
At the same time, when we don’t share a common language, it is the force that divides us.&lt;/p&gt;
&lt;p&gt;On office days, I like to go around the bakeries in China Town to get pastries for breakfast.
I always try to order in Chinese.
It was a little awkward at first but I’m getting better at it.&lt;/p&gt;
&lt;p&gt;Most of the locals in China Town are originally from Canton or Hong Kong.
I don’t really speak Cantonese but I know phrases.
I have my dim sum Cantonese and I know a few of the other foods too.
So, my first time ordering in China Town, I ordered in Cantonese-ish Mandarin.
I said something along the lines of: “一個叉烧包” but the “一個” part was in Mandarin
and the “叉烧包” was in Cantonese so the lady just kinda looked at me like WTF.
When it came time to pay, she said the price in Cantonese which I didn’t pick up on at all, so
I just gave her $5 and hopped for the best.
Now-a-days, I just order in Mandarin. I still get tripped up time to time
because I’m more used to saying certain words in Cantonese but I’m getting better.&lt;/p&gt;
&lt;p&gt;At another bakery, the lady at the counter greeted me in English,
but I was determined to practice.
She kinda gave me the “what?” look but I got that transaction more cleanly.&lt;/p&gt;
&lt;p&gt;I’m practicing Chinese again.
Last year, with my primary language exchange partner (my Mom) gone, I got rusty but I’m building it back up.&lt;/p&gt;
&lt;p&gt;About a month ago, I went to a club with my best friends girl friend.
It’s an interesting experience, hanging out with my friend’s girl friend (whose also my friend) one on one
but that’s a story for another time.
I had to check in my bag so I was talking to the coat check lady and she surprised me
by chatting me up in Mandarin.
She was from Hong Kong and was learning Mandarin, so she would take any opportunity to practice.
After that, any time I would go down stairs for the bathroom, I’d come and
talk to her a bit.&lt;/p&gt;
&lt;p&gt;At the end of the night, she opened up to me and told me that her brother had recently died from an over-dose.
I really wonder what made her feel comfortable enough to share that with me.
We were only strangers that shared a language in common.
Perhaps language is enough to feel a bond with someone else, especially in a foreign land.&lt;/p&gt;
&lt;p&gt;In contrast, language also blocks me from bonding with my grandmother.
She speaks Cantonese; I can’t speak much Cantonese.
We try to talk in Mandarin with sprinkled Cantonese words but in my 24 years, I still haven’t
had a meaningful conversation with her.
Sometimes I wonder what stories she might have that I’ve never heard before.
At one point, I heavily considered studying in Hong Kong for a summer to learn Cantonese but that
fell through.&lt;/p&gt;
&lt;p&gt;I did study in Taipei for a summer to learn Mandarin.
Living in Taipei is a little bizarre for me.
I look like a local but the way I dress gives away my American heritage.
I can get by okay with speaking but my literacy is terrible.&lt;/p&gt;
&lt;p&gt;Speaking Chinese or Spanish makes me really self conscious.
It was nerve wracking the first time I ordered in Chinese at those China Town bakeries.
From time to time, I consider ordering in Spanish at a Taco truck but I stop myself
because I feel like I’ll embarrass myself.
At least I look the part when I speak Chinese.
I feel like the Mexican cashier will just smile and reply in English.&lt;/p&gt;
&lt;p&gt;I wonder if it’s demeaning to speak someones native language to them in the States.
To me, it feels like I’m treating them as if they don’t speak English.&lt;/p&gt;
&lt;p&gt;The English language is one of the many ways that minorities and immigrants experience
discrimination in the US.
There is an episode of Planet Money called
&lt;a href=&quot;https://www.npr.org/2022/10/07/1127595393/taiwan-miracle-semiconductor-silicon-shield-china&quot;&gt;“Forging Taiwan’s Silicon Shield”&lt;/a&gt;
that talks about how Taiwanese Electrical Engineers felt that they could never move into managerial
roles in the States because their English wasn’t good enough.
To get that opportunity to move up, they had to move back to Taiwan to participate in the
semiconductor boom.&lt;/p&gt;
&lt;p&gt;My mom sometimes voices the same concerns of losing opportunity due to a language barrier.
She’s thought about getting a PhD before.
I’ve encouraged her too, I think she’s fully capable of it, but she won’t.
She says her English isn’t good enough.
It’s unfortunate that she’d deny herself the opportunity but that’s her reality.&lt;/p&gt;
&lt;p&gt;I suppose that’s how I feel in Taiwan.
I can get by but I feel limited in what I can and can’t do.
I wanted to go to concerts in Taipei but my Chinese isn’t good enough to figure out
showtimes and ticket purchases.
I want to rent an apartment but I’d never be able to read the whole lease agreement.
I want to make friends and meet locals but my Chinese isn’t good enough to have meaningful conversations.
That’s probably all in my head though.
If I can befriend the coat check lady at the club, I should be able to do
all that and more.&lt;/p&gt;</description><pubDate>Sun, 09 Oct 2022 00:00:00 GMT</pubDate></item><item><title>Photos of People</title><link>https://enochchau.com/blog/2022/photos-of-people/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/photos-of-people/</guid><description>&lt;p&gt;What is it about photos of humans that are so captivating?
When I see a photo, I like to imagine the story behind the frame.
Imagine all the pain, happiness, hopes, and dreams behind that person.&lt;/p&gt;
&lt;p&gt;Photos of things can be beautiful, but they aren’t relatable.
Things don’t change, they aren’t alive.
They have no emotions or wants, they might have stories, but usually
not as interesting as a living being.&lt;/p&gt;</description><pubDate>Mon, 03 Oct 2022 00:00:00 GMT</pubDate></item><item><title>Talking and Climbing</title><link>https://enochchau.com/blog/2022/talking-and-climbing/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/talking-and-climbing/</guid><description>&lt;p&gt;I just got back from my annual trip to Utah for the
&lt;a href=&quot;https://www.joesvalleyfest.com/&quot;&gt;Joe’s Valley Bouldering Festival&lt;/a&gt;.
When I say annual, it’s only the second year, but we had some real good rocks
and I met some real cool people.&lt;/p&gt;
&lt;p&gt;One of my favorite things about outdoor bouldering is meeting people at the crag.
Case in point, I met one of my good friends, Daniel, during the pandemic at Castle Rock.
Luckily, he asked to exchange phone numbers at the end of the session or else
I may have never seen him again.
Okay that’s a lie, I probably would have seen him again.&lt;/p&gt;
&lt;p&gt;Daniel and I were climbing at Mortar (in Berkeley) the other weekend and he basically
knew everyone at the crag.
He was talking to some people he knew from the gym, a group he met in
Tahoe, and a handful of other people he’s met from climbing around the Bay and Bishop.
The dude says he’s an introvert but I swear I’ve never seen him in his shell, if he even has one.
Chances are, I’d have met him again either at the gym or at the crag.&lt;/p&gt;
&lt;p&gt;That day I also met Mitch.
I wasn’t formally introduced to him but he sure was encouraging to me.
I got a lot of helpful “come on”s and beta from him.&lt;/p&gt;
&lt;p&gt;Interestingly, I first saw Mitch at the gym in Santa Clara.
We were working on problems next to each other (he’s much stronger than me), but
we didn’t exchange a single word.
For myself, in the gym, I never talk to people.
I like to think it’s because I came from lifting culture where you never really
talk to other people when lifting unless it’s:
“Hey bro, how many sets you have left?”.
So what’s so different about climbing at the crag?&lt;/p&gt;
&lt;p&gt;What is it about being outside that makes people so much social than being in the gym?
I can chalk it up to three things off the top of my head:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The density of people is much lower outdoors.&lt;/li&gt;
&lt;li&gt;The density of boulders is much lower.&lt;/li&gt;
&lt;li&gt;You rest way more.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the density of people is lower, we’re more inclined to talk to each other.
I’m more inclined to speak in small groups or even one on one conversations because
otherwise, we’d be siting in a small circle of awkward silence.&lt;/p&gt;
&lt;p&gt;Along with low people density, having less problems outdoors forces everyone to be together.
At the gym, there are many more options and I never stick around at a wall for very long.
At the crag, all the problems are spaced far apart, so I’m not going to be leaving in a hurry.
I project way harder outdoors so I’m gonna be sitting my ass down for awhile.
So why not talk to the other people who are also working on the same thing?&lt;/p&gt;
&lt;p&gt;Finally, you rest way more outdoors.
Outdoors is for the send.
I’m going take my 5 - 10 minute rests.
And I’m going to get bored because there’s no cell signal.
So of course, lets chat: about beta, about other rocks, about whatever it is strangers
with climbing in common talk about.&lt;/p&gt;</description><pubDate>Mon, 03 Oct 2022 00:00:00 GMT</pubDate></item><item><title>Confessions of a Former Coffee Purist</title><link>https://enochchau.com/blog/2022/coffee-confessions/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/coffee-confessions/</guid><description>&lt;p&gt;I used to think that coffee should only be enjoyed in it’s purest form: dark drip or straight espresso.
Any adulteration to this purity was blasphemy.
But I’ve been rehabilitated.&lt;/p&gt;
&lt;p&gt;These are the confessions of a former coffee purist.&lt;/p&gt;
&lt;p&gt;My coffee purity fanaticism started in my freshman year of high school.
My English teacher Ms. Kanshaw said
“You should learn to drink [coffee] black, you won’t always have cream and sugar”.
I took that to heart.
I wanted to seem tough and mature so, when I began my life long addiction, it was all black.&lt;/p&gt;
&lt;p&gt;Throughout college, I would only ever drink black coffee.
Mind you, it wasn’t good coffee, usually whatever dark roast I could find at the
grocery store.
My regular drink at Starbucks was a double shot of espresso, straight, sometimes with a few extra shots if it had been a long night.
I distinctly remember my brother’s graduation where his girl friend and my mom went to pick up
coffee and were surprised when my order ended up being a little cup of black liquid.
I was so cool right? (No not really.)&lt;/p&gt;
&lt;p&gt;My grocery store and Starbucks days went on for a few years, but eventually,
I got wind of the 3rd wave coffee scene and all the delicious fruity, chocolaty beans that came along with it.
Around that same time, I also took up cappuccinos.
Probably something about cappuccinos being ingrained in Italian coffee culture.
I even looked down on things like Philz. Their added flavoring seemed to be
spitting in the face of the roasters who studied their craft to draw out those same flavors.
I wanted to be someone who not only drank coffee, but knew coffee.
Coffee was an integral part of my personality.
My addition was becoming an obsession.&lt;/p&gt;
&lt;p&gt;But something in me slowly changed.
I’ve come to realize that while there are good and bad cappuccinos, all the good ones taste the same.
Locally roasted ethically sourced Ethiopian single origin will always be delicious,
but I’ll take a cup of Folgers if it gets me the caffeine I need.
Well roasted beans can bring out interesting flavors but Philz’s added flavoring is just
as novel a taste.
I’ve even come to accept flavored coffee drinks and other derivatives and
I’d like to tell you about some of my recent favorites.&lt;/p&gt;
&lt;h2 id=&quot;coffee-bar&quot;&gt;Coffee Bar&lt;/h2&gt;
&lt;p&gt;One of my frequent haunts is Coffee Bar Kearny on Kearny St. in San Francisco.
I’ve chatted up the barista a few times and I’ve found that
they make both their vanilla syrup and caramel in house.
You can see the vanilla beans suspended in translucent jars just across the counter.
He also told me they’re coming up with a spiced ginger inspired syrup soon.
I’m hoping to get to try that on my next visit.&lt;/p&gt;
&lt;p&gt;This here is their vanilla latte.
It was deliciously sweet with a whiff of vanilla creeping up my nose and through my mouth.
Not much coffee bitterness, but delicious none the less.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_coffee_bar_vanilla_latte.BZVlO3vL_Ct6ki.webp&quot; alt=&quot;Coffee Bar Vanilla Latte&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;h2 id=&quot;soon-soon-souvenir-shop&quot;&gt;Soon Soon Souvenir Shop&lt;/h2&gt;
&lt;img src=&quot;/_astro/01_soon_soon.Bz1yAFn7_GCfdo.webp&quot; alt=&quot;Soon Soon Souvenir Shop&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;A new coffee shop for me is Soon Soon in San Francisco’s China Town.
They call it a souvenir shop but that’s probably just a play on the myriad of nearby souvenir shops
that prey on the China Town tourists. (More power to you 阿姨, you take those tourists money.)&lt;/p&gt;
&lt;p&gt;Soon soon has a couple of flavored drinks but the one I tried is the Tiger mocha with
flecks or orange syrup floating on top of a foamy mocha.
I’ll say, I’m not too big a fan of mochas, the cocoa powder can be a bit grainy.
I’ll also say though, the acidity of the orange syrup really complimented the
richness of the chocolate.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_soon_soon_tiger_mocha.DtNK2ZHm_XNxHW.webp&quot; alt=&quot;Tiger Mocha&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;By the way, I love their double happiness cup design.&lt;/p&gt;
&lt;h2 id=&quot;the-coffee-movement&quot;&gt;The Coffee Movement&lt;/h2&gt;
&lt;img src=&quot;/_astro/05_coffee_movement.BKdxUwiJ_2vw83B.webp&quot; alt=&quot;The Coffee Movement&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Finally, one of my favorite coffee shops, The Coffee Movement on the outskirts
of China Town.
My friend &lt;a href=&quot;https://wjgan.com/&quot;&gt;Gan&lt;/a&gt; who lives around these parts also really likes this place.
He has yet to lead me astray.
And just look at the line in the photo!
Solid coffee and $3.50 cappuccinos.
That’s cheap for the city.&lt;/p&gt;
&lt;p&gt;This coffee shop used to have an old VW bus parked out front that you could drink your coffee in,
but it got replaced by the parklet about a month ago.
The parklet is more spacious but the bus had better vibes.&lt;/p&gt;
&lt;p&gt;I got to try an orange coffee cream soda here!
Isn’t it a beaut?
That day was quite hot so it was nice to sip on during my walk to work.
Orange cream soda is already delicious and adding coffee to the mix just makes it that much better.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_coffee_movement_organge_coffee_cream_soad.D_0eU441_pjEiv.webp&quot; alt=&quot;Coffee orange cream soda&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;And here it is all mixed up.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_coffee_soda_stired.WCIolQVZ_EpXLg.webp&quot; alt=&quot;Coffee soda stirred&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I’ve been drinking a fair amount of “coffee soda” lately.
At the office, we have these little bottles of Blue Bottle cold brew that I’ll mix
with lemon sparkling water.
Don’t knock it till you try it.
Coffee soda is quite the refreshing way to feed my addiction.&lt;/p&gt;
&lt;p&gt;I hope you too can find some love in drinking delicious coffee drinks (or find love in general for that matter).&lt;/p&gt;</description><pubDate>Tue, 13 Sep 2022 00:00:00 GMT</pubDate></item><item><title>Sqlite Migrations with Golang on Fly.io</title><link>https://enochchau.com/blog/2022/sqlite-migrations-flyio/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/sqlite-migrations-flyio/</guid><description>&lt;p&gt;I’ve been developing a &lt;a href=&quot;https://github.com/enochchau/rss-server&quot;&gt;simple RSS reader&lt;/a&gt; in Go.
Usually, for a small personal project like this, I’d use Heroku for a fast simple deployment.
But with &lt;a href=&quot;https://help.heroku.com/RSBRUH58/removal-of-heroku-free-product-plans-faq&quot;&gt;Heroku’s free tier being sunset&lt;/a&gt;,
I’ve been looking for a replacement platform.
I came to choose between two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://render.com/&quot;&gt;Render&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fly.io/&quot;&gt;Fly.io&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m pretty cheap, so for a toy app like this, I only considered free options.
I don’t need much compute but I do need some form of persistent storage.
Both of these platforms have free PostgreSQL offerings but Fly.io’s is much better.
On the free plan, Render will &lt;a href=&quot;https://render.com/docs/free#free-postgresql-databases&quot;&gt;delete your data after 90 days&lt;/a&gt;.
On the other hand Fly.io offers &lt;a href=&quot;https://fly.io/docs/about/pricing/&quot;&gt;up to 3GB&lt;/a&gt; of persistent volume storage.
Although I used PostgreSQL as a measurement for which provider to choose, I ultimately didn’t use PostgreSQL.&lt;/p&gt;
&lt;p&gt;Developing locally with Sqlite is a breeze.
The developer experience of having the entire database being a single file is too easy to pass up.
After my initial prototype, I considered switching the database to PostgreSQL.
This wouldn’t have been too difficult with Go’s (more or less) database agnostic &lt;code&gt;database/sql&lt;/code&gt; library
but I decided against it so I could maintain my simple local development workflow.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fly.io/docs/rails/advanced-guides/sqlite3/&quot;&gt;Setting up a Sqlite database&lt;/a&gt; on Fly.io is pretty easy.
First I needed to create a persistent volume.
Here, I’m creating a volume called “database” with a size of 1GB (the largest size available in the free tier.)&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;fly&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; volumes&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; database&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --size&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, I can mount my volume on my app by adding the following to the &lt;code&gt;fly.toml&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;mounts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  source&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;database&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  destination&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/mnt/database&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, I can point my app to use this volume to create it’s Sqlite database.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;go&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;database/sql&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  db&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; :=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; sql&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;Open&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;sqlite3&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/mnt/database/rss-server.sqlite&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now our data will persist across deployments.&lt;/p&gt;
&lt;h2 id=&quot;deploy&quot;&gt;Deploy&lt;/h2&gt;
&lt;p&gt;To deploy my app, I can either build my app with Fly.io’s provided
&lt;a href=&quot;https://fly.io/docs/getting-started/golang/&quot;&gt;build pack&lt;/a&gt; or write my own &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Dockerfile&lt;/a&gt;.
Initially, I went with the build pack since that seemed like the simplest and quickest thing to do.
I got my app built and deployed by running a single CLI command.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;flyctl&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; launch&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But after the deployment, attempting to ping the server returned this error:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;no such table: rss_feeds&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It seems that I overlooked that my Sqlite database hasn’t been migrated yet!&lt;/p&gt;
&lt;p&gt;For local development, I’ve been using &lt;a href=&quot;https://github.com/golang-migrate/migrate&quot;&gt;golang-migrate/migrate&lt;/a&gt;
as a library to write a migration binary.
This works fine locally since I have my SQL files contained within the local repo but it starts to fall
apart for the build pack.
With the build pack, I can only deploy the single RSS server binary.
Luckily, Fly.io allows you to &lt;a href=&quot;https://fly.io/docs/reference/builders/#dockerfile&quot;&gt;write your own Dockerfile&lt;/a&gt;
for deployments.&lt;/p&gt;
&lt;p&gt;With my own Dockerfile, I now had a couple of choices to perform the migration:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Package the Sqlite binary in my Dockerfile to run the migration SQL files.&lt;/li&gt;
&lt;li&gt;Package golang-migrate/migrate’s CLI application to run the migrations.&lt;/li&gt;
&lt;li&gt;Package the migration binary I’ve been using locally along with my SQL migration files.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I went with the third option as this is what was closet to my local development work flow.
Adding a second binary along with the migration SQL files was easy to do with a builder image.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;docker&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# syntax=docker/dockerfile:1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; golang:1.19-alpine &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; builder&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;WORKDIR&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /opt/rss-server/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# add gcc for CGO&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; apk add build-base&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;COPY&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; . .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# build the main rss-server binary&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; go build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# build the migration binary&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; go build -o migrate cmd/migrate/main.go&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; alpine:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;WORKDIR&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /opt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# copy over the binaries we built in the last step&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;COPY&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; --from=builder /opt/rss-server/rss-server ./&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;COPY&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; --from=builder /opt/rss-server/migrate ./&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# don&amp;#39;t forget to copy over the SQL migration files.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;COPY&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; --from=builder /opt/rss-server/db/migrations ./db/migrations&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;CMD&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;./rss-server&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all that done, after a deployment, I can perform the migration by SSH-ing into the
running container and running my migration binary.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;flyctl&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; ssh&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; console&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;/opt/migrate&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One issue I see with using Sqlite is that I may have some difficulty extracting
my data down the road.
There’s currently no way for me to get my Sqlite database off of Fly.io.
I’d imagine, since SSH is possible, that SCP should be possible too but
there’s currently no indication in the docs.
Fly.io is still pretty new so they might address this later down the line.
Since this is still just a toy app, I’m not too concerned.&lt;/p&gt;
&lt;p&gt;The whole experience of deploying my app was very easy.
Fly.io’s &lt;code&gt;flyctl&lt;/code&gt; CLI makes it easy to make updates to my app.
Everything feels pretty close to the simple developer experience
of &lt;a href=&quot;https://devcenter.heroku.com/articles/git#deploy-your-code&quot;&gt;&lt;code&gt;git push heroku main&lt;/code&gt;&lt;/a&gt;.
I especially liked that I could build my own Docker image.
In the future, I can see myself continuing to use Fly.io to deploy small personal projects.&lt;/p&gt;</description><pubDate>Tue, 06 Sep 2022 00:00:00 GMT</pubDate></item><item><title>Japanese Iced Coffee</title><link>https://enochchau.com/blog/2022/japanese-iced-coffee/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/japanese-iced-coffee/</guid><description>&lt;p&gt;It’s been really hot in the Bay Area recently.
We’ve hit temps of high 80°F during the day, a rare departure from the consistent 60°F days.
With hot weather, comes ways to escape the heat!
This was a great great opportunity to make some cold drinks.&lt;/p&gt;
&lt;p&gt;Today, we’re making Japanese iced coffee.
It’s a pretty simple recipe of equal parts ice and water along with the regular ratio of coffee.&lt;/p&gt;
&lt;p&gt;First things first, I need some ice, but there’s never any ice at home.
I told my dad that I missed two things about apartment living in college.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There was always ice in the freezer.&lt;/li&gt;
&lt;li&gt;There were never any pots or pans in the oven.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m not sure if the ice thing is an Asian thing but I think the pots and pans in the oven thing
is definitely an Asian thing.
I’d love to hear your opinion on this, just so I can verify that I’m not crazy.
(Shoot me a ping on IG &lt;a href=&quot;https://www.instagram.com/uhhuhhuny/&quot;&gt;@uhhuhhuny&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;So first things first, lets make some ice cubes.
I’ve got some neat-o ice cube trays from Ikea.
Pretty sure we’ve had them since I was a kid.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_icecubes.BF2MSkd8_ZIthPf.webp&quot; alt=&quot;icecube trays&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Now, lets make the coffee.
I’m using a Melitta but any pour over cone will work.
My ratio is 400g of water to 24g of coffee.
That means I’ll use about 200g of ice and then brew with 200g of water.
Lately, I’ve been using a little less ice (140g) and a little more hot water (260g) because I just can’t get
enough ice out of those ice trays.&lt;/p&gt;
&lt;p&gt;First, I’ll grind all my coffee, rinse the filter, and put the ice in the cup.
Put the coffee in the cone then start with an initial pour of about 30g - 40g.
Bloom the coffee for about 30 seconds.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_bloom.B0w4zgfT_ZYATWI.webp&quot; alt=&quot;bloom the coffee&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Next, I’ll do a few more incremental pours to reach my target amount of water.
I’m never super consistent with my pours but I try to go for 1/3 of the water every 1 minute or so.
I aim for a total brew time of around 3 minutes.&lt;/p&gt;
&lt;img src=&quot;/_astro/01_pour.C6VWZLYP_28lTg4.webp&quot; alt=&quot;brewing coffee&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;And we’re done!
By the way, that’s a Starbucks cup but I’m not huge on Starbucks.
My mom’s an Elementary school teacher and she gets a ton of Starbucks gift cards
as gifts from her student’s parents.
We don’t go to Starbucks that much so she uses the gift cards to buy cups.
If you’re thinking about becoming an elementary school teacher, the gift cards are a pretty nice bonus.
She’ll get around $300 in assorted gift cards at Christmas and again at the end of the school year.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_done.BHM0YICC_KiaQN.webp&quot; alt=&quot;done&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Since it’s been consistently really hot for the past few days, here’s another photo
of Japanese iced coffee in a glass.
I’m not really sure why it’s Japanese in origin but I did
first see it in a &lt;a href=&quot;https://youtu.be/qIbbJV-grHg&quot;&gt;Worth It Buzz Feed video on Coffee in Tokyo&lt;/a&gt;.
Ahhh, here I go again, getting nostalgic about 2016 - 2019 YouTube.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_glass.YFb18u5q_Z1n0mVx.webp&quot; alt=&quot;coffee in a glass&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Tue, 06 Sep 2022 00:00:00 GMT</pubDate></item><item><title>Micro Frontends with Astro</title><link>https://enochchau.com/blog/2022/micro-frontends-with-astro/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/micro-frontends-with-astro/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/micro-frontends-astro&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://enochchau.github.io/micro-frontends-astro/&quot;&gt;demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not too long ago, I rebuilt this website using Astro.
&lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; is a new multi-page app (MPA) framework that just hit 1.0 recently.
The neat part of Astro is that it can
&lt;a href=&quot;https://docs.astro.build/en/core-concepts/framework-components/&quot;&gt;render components from different frameworks&lt;/a&gt; within the same app.
It defaults to rendering everything as static HTML which is then hydrated by isolated “islands” of JavaScript.
Using this multi-framework capability, we can use Astro to build a &lt;a href=&quot;https://micro-frontends.org/&quot;&gt;micro-frontend&lt;/a&gt; application.&lt;/p&gt;
&lt;p&gt;Say we have a team of engineers that all work on an online storefront.
There might be a team that builds the shopping part of the site.
This part of the app shows the catalog of items.
A catalog can better utilize SSR since there isn’t much dynamic rendering needed to display each item.
The shop team could be using Astro components with React components to build their SSR rendered pages.&lt;/p&gt;
&lt;p&gt;Another part of our storefront could be for real time data visualization of what customers are viewing.
This team might need to build a performance intensive table and graph with frequent updates.
They could use a framework like &lt;a href=&quot;https://www.solidjs.com/&quot;&gt;SolidJS&lt;/a&gt; or &lt;a href=&quot;https://svelte.dev/&quot;&gt;Svelte&lt;/a&gt;
for faster render times.&lt;/p&gt;
&lt;p&gt;For this &lt;a href=&quot;https://github.com/enochchau/micro-frontends-astro&quot;&gt;demo&lt;/a&gt;,
we will be building a part React, part SoildJS mixed micro-frontend.
Since this is a shared app, we can have shared components such as a Navigation Bar rendered with Astro.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;astro&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Layout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NavigationBar&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ReactApp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Layout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our Astro app will have the following &lt;code&gt;pages/&lt;/code&gt; &lt;a href=&quot;https://docs.astro.build/en/core-concepts/routing/#static-routes&quot;&gt;routing structure&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;pages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| - react&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|     | - [...route].astro&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| - solid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      | - [...route].astro&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the initial server side rendering, we can hand client side routing over to the framework.
Navigating to &lt;code&gt;/react&lt;/code&gt; will allow us to render the React app’s root.
Once the root is rendered, react-router will take over client side routing.
There’s only one issue.
Astro renders all components to static HTML by default,
but Astro doesn’t really understand how to render react-router to static HTML.&lt;/p&gt;
&lt;img src=&quot;/_astro/react-router-ssr-fail.Brh49R0y_rmmb1.webp&quot; alt=&quot;React router failed SSR&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;450&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;To fix this issue, we need to get Astro to skip SSR on this component
by using the &lt;a href=&quot;https://docs.astro.build/en/reference/directives-reference/#clientonly&quot;&gt;&lt;code&gt;client:only&lt;/code&gt;&lt;/a&gt; directive.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;astro&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ReactApp&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; client:only&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;react&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One more thing we need to do for proper routing is to tell Astro which routes should be handled
by react-router.
Using &lt;a href=&quot;https://docs.astro.build/en/core-concepts/routing/#dynamic-routes&quot;&gt;dynamic routing&lt;/a&gt;, we can
specify a list of pages that should load our React app.
Using the &lt;a href=&quot;https://docs.astro.build/en/core-concepts/routing/#dynamic-routes&quot;&gt;&lt;code&gt;getStaticPaths()&lt;/code&gt; function&lt;/a&gt;,
we can tell Astro which pages should be controlled by react-router, in this case, we want
&lt;code&gt;/react&lt;/code&gt; and &lt;code&gt;/react/page1&lt;/code&gt; to be controlled client side.
Now, if we navigate to &lt;code&gt;/react/page1&lt;/code&gt; or &lt;code&gt;/react&lt;/code&gt;, Astro will be able to correctly load the React app.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;astro&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; Layout&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;../../layouts/Layout.astro&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; NavigationBar&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;../../components/NavigationBar.astro&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;../../components/ReactApp&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; getStaticPaths&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // we need to define all the possible react routes so astro knows to redirect here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; [{ &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;params&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;route&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;undefined&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } }, { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;params&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;route&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;page1&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } }];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Layout&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; title&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;React&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;NaviationBar&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ReactApp&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; client:only&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;react&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Layout&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within our app, we can use react-router to handle client side routing for our React App.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;/** &lt;/span&gt;&lt;span style=&quot;color:#C678DD;font-style:italic&quot;&gt;@jsxImportSource&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt; react */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; React&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;react&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;useRoutes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;BrowserRouter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;react-router-dom&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; rootRoute&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;/react&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; routes&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;rootRoute&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    element&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Root&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  { &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;rootRoute&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;/page1&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;element&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Page1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&amp;gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Routes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  let&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; element&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; useRoutes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;routes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; element&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; ReactApp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;BrowserRouter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;The rest of this page is rendered by React&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Routes&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;BrowserRouter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ve now configured one route in our MPA Astro app to load a React SPA.
The &lt;a href=&quot;https://github.com/enochchau/micro-frontends-astro&quot;&gt;demo&lt;/a&gt; code goes into more detail on the SolidJS part of the app,
but it follows the same general outline.&lt;/p&gt;
&lt;p&gt;One downside of a MPA is that we can’t use a client side store like Redux or MobX.
We can use a client side store within just our React app or just our SolidJS app but navigating between frameworks or
Astro pages will not persist a client side store.
This is a general issue with micro-frontends that &lt;a href=&quot;https://single-spa.js.org/&quot;&gt;single-spa&lt;/a&gt;
can &lt;a href=&quot;https://single-spa.js.org/docs/recommended-setup/#state-management&quot;&gt;explain better&lt;/a&gt;.
But this would be a problem anyways with traditional methods of creating mixed frontends.&lt;/p&gt;
&lt;p&gt;Traditionally, we could create a structure like this by using a proxy like NGINX to send the client different
bundles depending on the route.
Each bundle would have to be contained in a separate repository, built separately, and deployed separately.
The benefit that Astro provides is that we can keep all our bundles in the same repository.
We can also build the entire application with a single build command.
We don’t have to setup any special network rules or additional infrastructure.
All our code is contained in a single MPA and easily handled by any of Astro’s &lt;a href=&quot;https://docs.astro.build/en/guides/deploy/&quot;&gt;deployment methods&lt;/a&gt;.&lt;/p&gt;</description><pubDate>Sat, 03 Sep 2022 00:00:00 GMT</pubDate></item><item><title>Find Your Music</title><link>https://enochchau.com/blog/2022/find-your-music/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/find-your-music/</guid><description>&lt;p&gt;Break free from the algorithm.
Find your own damn music.&lt;/p&gt;
&lt;p&gt;A lot of us youngins today get all our music recommendations from the algorithm,
whether that be Spotify’s Discover Weekly, YouTube recommendations, or endless
TikTok scrolling.&lt;/p&gt;
&lt;p&gt;At some point recommendations stagnate.
They’re impersonal by nature and restrictive by design.
You get shown what you like with little room for discovery outside this profit zone.
Algorithmic recommendations can fill the void for the time being but I quickly forget
these impersonal discoveries.&lt;/p&gt;
&lt;p&gt;We, as humans, can tie our senses to specific memories.
Certain smells or sounds will bring me back to a distant bygone time.
Hearing an old favorite song can bring back all the times I laughed or cried listening to it.
My favorite songs never came from an algorithm.
My &lt;a href=&quot;https://open.spotify.com/album/78lIjVFQSSRqO47Pvw1b9M?si=SVNdmpedTNOoLDCJ4HzN5Q&quot;&gt;favorite songs&lt;/a&gt;
were introduced to me by my close friends.
The music I can remember and come back to listen to again and again
were my own discoveries.&lt;/p&gt;
&lt;p&gt;So how can we break free from the algorithm?
From experience, there’s a few different ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Friends&lt;/li&gt;
&lt;li&gt;Features&lt;/li&gt;
&lt;li&gt;Discover something through pure coincidence&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;friends&quot;&gt;Friends&lt;/h2&gt;
&lt;p&gt;When I meet someone new, I like to ask “What kind of music do you listen to?”.
I hate it when they respond with “everything”.
That’s such a cop out answer because the truth is you don’t like everything.
People are a bit bashful to strangers, as if I’ll judge them based on their taste in music.
I mean, I will judge them but that’s okay because I would have figured that out anyways
after getting to know them better.
The most fun is when I can get a new friend to show my their Liked Songs and personal playlists.
There’s always something good hidden in there.&lt;/p&gt;
&lt;p&gt;Another place to share music is the car.
When I’m the driver I like to just play whatever music I’m currently listening to.
Screw the “car playlists” with popular music for the normies.
You get to listen to whatever is currently on my palette, be it
&lt;a href=&quot;https://open.spotify.com/track/5iEOCpoeZDAGae1IycuPxh?si=f36805dcefaa4b03&quot;&gt;rock&lt;/a&gt;,
&lt;a href=&quot;https://open.spotify.com/track/6qIqtnhj9hhpkPuIlR4fDd?si=8fd2c06d365a406f&quot;&gt;EDM&lt;/a&gt;,
&lt;a href=&quot;https://open.spotify.com/track/5hkloqRyof9EI59BWxPcQQ?si=2713b15e6dc74c0a&quot;&gt;jazz&lt;/a&gt;,
or
&lt;a href=&quot;https://open.spotify.com/track/2UNzxGjTGwFEkRBjsvfD9S?si=6d54b052e7284145&quot;&gt;k-pop&lt;/a&gt;.
It’s my car and you’re my captive audience.&lt;/p&gt;
&lt;p&gt;At the same time, I love it when I’m in the passenger seat and I get played some
niche music I’ve never heard before.
I’ll try to get in a sneaky Shazam or just straight up ask for the song.
It’s a great way to start a deeper conversation.&lt;/p&gt;
&lt;p&gt;You give me recommendations, I give you recommendations.
I might not like all the music you listen to and you might not like all my music,
but sometimes, there’s a magical thing that can happen.
Where there’s a wonderful song that we both love.&lt;/p&gt;
&lt;h2 id=&quot;features&quot;&gt;Features&lt;/h2&gt;
&lt;p&gt;Features are a great way to expand your musical tastes.
Crazy stuff like Van Halen on Michael Jackson’s ‘Beat It’ blew that song off the top charts.
It introduced a bunch of metal heads to pop and visa versa.&lt;/p&gt;
&lt;p&gt;More recently, I’ve found a few interesting Taiwanese artists from Elephant Gym’s latest album.
The track ‘&lt;a href=&quot;https://open.spotify.com/track/3WPqLp1kDaFZa6PTlYQ4dV?si=fb3e5226e1a24cd4&quot;&gt;Shadow&lt;/a&gt;’
features and a Taiwanese Jazz artist named 9m88.
She’s got some pretty good music, but from there I jumped again to find a
mash up rap/jazz &lt;a href=&quot;https://www.youtube.com/watch?v=Tca7G95LgFc&amp;feature=emb_title&quot;&gt;song she did with Kumachan&lt;/a&gt;.
(If you don’t understand Chinese, you should look up the lyrics, this song is actually pretty funny.)
But from Kumachan, I found yet another good &lt;a href=&quot;https://open.spotify.com/track/3ar8egO3eEowO02iMx1Ktu?si=4feb34e5f5f64f57&quot;&gt;song that he did with Julia Wu&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So just to recap:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Elephant Gym -&amp;gt; 9m88 -&amp;gt; Kumachan -&amp;gt; Julia Wu&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides features, another good method of finding artists is from festival lineups.
I remember hearing about Wednesday Campanella from her first appearance at Camp Flog Gnaw in 2016.
&lt;a href=&quot;https://www.joannwphotos.com/&quot;&gt;Joann&lt;/a&gt; introduced me to Kyary Pamyu Pamyu after seeing her in the lineup for Second Sky.
I don’t think I would have found either of these artists just by trawling around the internet.
So thanks Tyler, and thanks Porter.&lt;/p&gt;
&lt;h2 id=&quot;coincidental-discovery&quot;&gt;Coincidental Discovery&lt;/h2&gt;
&lt;p&gt;Coincidental discovery is probably the hardest way to discover music.
I would count this as anything that doesn’t come from a purely music focused background.&lt;/p&gt;
&lt;p&gt;For me this usually comes from Shazaming some random background music.
For example, I recently found this banger of a
&lt;a href=&quot;https://youtu.be/TUTxdXI6jHg?list=LL&quot;&gt;K-mart 90’s song&lt;/a&gt; from the end of this
&lt;a href=&quot;https://youtu.be/4wvAwVwc6sY?t=1385&quot;&gt;Climate Town video&lt;/a&gt;.
Or the time I heard ‘&lt;a href=&quot;https://open.spotify.com/track/0DJSxaA7mYjpyuI19uClRq?si=48bbdfa49cb74456&quot;&gt;I Love Poland&lt;/a&gt;’
inside a bluk clothing shop in the Taipei underground.&lt;/p&gt;
&lt;p&gt;But my favorite story of coincidental discovery is the &lt;a href=&quot;https://coocoobirds.bandcamp.com/&quot;&gt;Coo Coo Birds&lt;/a&gt;.
Sometime in high school, I went to a random one off show at the Rick Shaw Stop.
I didn’t know the lineup, or the theme, I just wanted something to do.
And who else was playing but the Coo Coo Birds with their psychedelic rock and brash attitudes.
It’s a night I quickly forgot because I was tripping on acid but the next day
I found two of my high school anthems,
‘&lt;a href=&quot;https://coocoobirds.bandcamp.com/track/banged-a-booker&quot;&gt;Banged A Booker&lt;/a&gt;’ and
‘&lt;a href=&quot;https://coocoobirds.bandcamp.com/track/caffeine-and-ketamine&quot;&gt;Caffeine and Ketamine&lt;/a&gt;’.
That’s all to say that good songs can come from bizarre places.&lt;/p&gt;
&lt;p&gt;I would also count things like movie and video game sound tracks in this category.
You weren’t looking for it but damn
&lt;a href=&quot;https://open.spotify.com/track/1iDt0utPzajZLQH2OLu16e?si=a9659ce5449a4236&quot;&gt;that track&lt;/a&gt;
from that game was a banger.&lt;/p&gt;
&lt;h2 id=&quot;algorithms-arent-all-bad&quot;&gt;Algorithms Aren’t All Bad&lt;/h2&gt;
&lt;p&gt;Algorithms are still a good starting point to find music.
I’ve found several artists that I do like from my Spotify Discover Weekly, but
I try to use it as a starting point and not the entire journey.&lt;/p&gt;
&lt;p&gt;We’d all benefit a little from talking to other humans and music is a great way to start a conversation.&lt;/p&gt;</description><pubDate>Wed, 31 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Sight Glass: Blueboon</title><link>https://enochchau.com/blog/2022/blueboon/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/blueboon/</guid><description>&lt;p&gt;I love coffee.
Today, I just finished a bag of Sight Glass’s Blueboon blend.
It’s one of the better blends I’ve had.&lt;/p&gt;
&lt;p&gt;Sight Glass’s &lt;a href=&quot;https://sightglasscoffee.com/blogs/shops/divisadero&quot;&gt;location on Divisadero&lt;/a&gt; in San Francisco is beautiful.
I highly recommend a visit if you’re in the area.
They’ve got some delicious pastries and one of my favorite cappuccinos.
I’ve taken standup in this coffee shop, met an old friend at this coffee shop,
had several cups of coffee in this coffee shop,
contemplated my future in this coffee shop.
I love this coffee shop, but mostly just the coffee.&lt;/p&gt;
&lt;p&gt;I love this part of the city.
This portion of Divis, all the way down to the Duboce Triangle has some excellent vibes and restaurants.
A highlight is Duboce Park with all its good boys and girls (read: dogs).&lt;/p&gt;
&lt;img src=&quot;/_astro/00_front.BdhUbbOQ_1ug6dS.webp&quot; alt=&quot;Blueboon Coffee Bag Front&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/01_back.BMb-Icjd_ZUaerH.webp&quot; alt=&quot;Blueboon Coffee Bag Back&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;My current methods of coffee brewing are an Aeropress and a Melitta pour over cone.
Each brew method produces slightly different tasting notes.&lt;/p&gt;
&lt;h2 id=&quot;aeropress&quot;&gt;Aeropress&lt;/h2&gt;
&lt;img src=&quot;/_astro/02_aero_press.BegojTo4_Z1RecAC.webp&quot; alt=&quot;Aeropress&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;My Aeropress ratio is 12g coffee to 200g of water which is adapted from &lt;a href=&quot;https://www.youtube.com/watch?v=j6VlT_jUVPc&quot;&gt;James Hoffman’s recipe&lt;/a&gt;.
Coffee YouTube is pretty fun. James Hoffman’s channel really blew up in that 2018-2019 golden age of YouTube.
I’d like to say I was an early subscriber but I probably hopped on the wagon around this same time.&lt;/p&gt;
&lt;p&gt;For grind size, I use a Timemore Chestnut C2 at about 13 - 14 clicks.
I adjust the grind size depending on the coffee but it’s usually around this mark.
The Chestnut C2 was also a recommendation from one of Hoffman’s videos.
I’m sure you’re starting to see a trend here.&lt;/p&gt;
&lt;p&gt;I can get a pretty consistent cup from an Aeropress but the coffee ends up being a bit lighter overall.
I get more of the floral and fruity notes and less of the chocolaty toffee notes.&lt;/p&gt;
&lt;h2 id=&quot;melitta&quot;&gt;Melitta&lt;/h2&gt;
&lt;img src=&quot;/_astro/04_melitta.CJ0QxNWm_Z13jdil.webp&quot; alt=&quot;Melitta&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;My Melitta ratio is 15g coffee to 250g of water.
This ratio is adapted from a few different sources including
‘&lt;a href=&quot;https://www.goodreads.com/en/book/show/34144491-craft-coffee&quot;&gt;Craft Coffee&lt;/a&gt;’ by Jessica Easto
(a gift from Rebecca)
and probably some of James Hoffman’s videos.&lt;/p&gt;
&lt;p&gt;The chrome mug in the photo is the vessel portion of a French Press.
I use it to pour hot water.
I know that I lose some water temperatures during the transfer but I don’t have a better way
to pour hot water right now.
Back in college, I’d boil water with a &lt;a href=&quot;https://enochchau.com/_image/pages/create/pot_800x799.webp&quot;&gt;pot&lt;/a&gt;
to get those hot hot temps but I’m a little too old to care now.&lt;/p&gt;
&lt;p&gt;For grind size, I use anywhere between 15 to 19 clicks.
There’s much more variance in grind size because I have to make sure I don’t get any stalling on the pour.
If the grind is too fine, the water flow can get backed up and slow down the brew.
Today, we’re going with 16 clicks.
Compared to the Aeropress, coffee made with the Melitta aren’t as consistent because of the aforementioned stalling.&lt;/p&gt;
&lt;p&gt;The coffee typically has more body compared the Aeropress.
I get more of the chocolaty rich flavors and less of the floral, fruity flavors.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_coffee.BwHKCyBz_ZIzdun.webp&quot; alt=&quot;Coffee&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Couldn’t ask for a better way to start my day.
Tomorrow, I’ve got a new bag of coffee to try but I’ll keep that as a surprise.&lt;/p&gt;</description><pubDate>Tue, 30 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Making a Telescope.nvim Extension</title><link>https://enochchau.com/blog/2022/telescope-plugin/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/telescope-plugin/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/telescope-node-workspace.nvim&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At &lt;a href=&quot;https://gatsby.events&quot;&gt;Gatsby&lt;/a&gt; we use TypeScript across the stack in a
&lt;a href=&quot;https://github.com/yarnpkg/berry&quot;&gt;Yarn (Berry)&lt;/a&gt; PnP monorepo.
There are over 50 packages at this point and being able to navigate through
these packages quickly is important for my workflow.&lt;/p&gt;
&lt;p&gt;On the command line, I have various Bash functions to fly around.
For example, the completion scripts I have setup allow me to quickly move between internal packages.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# cd into repo/packages or any subdirectory&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;cdgp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; gitroot&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; rev-parse &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;--show-toplevel&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;  cd&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;$gitroot&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;/packages/&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;_cdgp_completion&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; gitroot&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; rev-parse &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;--show-toplevel&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  COMPREPLY&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;$(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;ls&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; $gitroot&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;/packages/)&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;complete&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -F&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; _cdgp_completion&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; cdgp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In contracts, for Neovim, I have been painstakingly using &lt;code&gt;:cd ../../packages&lt;/code&gt; or some derivative to navigate.
This gets slow and cumbersome if my current working directory is too deeply nested.&lt;/p&gt;
&lt;p&gt;I need something to move my &lt;code&gt;cwd&lt;/code&gt; around and
&lt;a href=&quot;https://github.com/nvim-telescope/telescope.nvim&quot;&gt;Telescope.nvim&lt;/a&gt;
happens to be just the ticket.
Telescope.nvim is a modern extendable fuzzy finder plugin for Neovim.
The idea is to create a plugin that can find every package in the workspace,
show them in the Telescope picker, and then change my &lt;code&gt;cwd&lt;/code&gt; on selection.&lt;/p&gt;
&lt;p&gt;Often times, with personal projects, I bite off more than I can chew.
I set lofty goals and then give up when I feel like progress is too slow.
So to get started quickly, I would only support the bare necessities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only Yarn Berry Support&lt;/li&gt;
&lt;li&gt;Everything lives in my dotfiles&lt;/li&gt;
&lt;li&gt;No file preview is necessary, just a picker&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Setting up these ground rules helped me avoid getting blocked too early.&lt;/p&gt;
&lt;h2 id=&quot;getting-workspace-packages&quot;&gt;Getting Workspace Packages&lt;/h2&gt;
&lt;p&gt;Before I can get into creating a plugin, I need to figure out a way to extract a list of workspace packages.
Yarn Berry comes with the handy &lt;a href=&quot;https://yarnpkg.com/cli/workspaces/list#gatsby-focus-wrapper&quot;&gt;&lt;code&gt;yarn workspaces list&lt;/code&gt;&lt;/a&gt;
command which gives an easily parsable output.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;~/code/yarn-v2-workspace ❯ yarn workspaces list&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;➤&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; YN0000:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;➤&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; YN0000:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; packages/a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;➤&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; YN0000:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; packages/b&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;➤&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; YN0000:&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; Done&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; 0s&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; 3ms&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But we can make it much easier to parse with the &lt;code&gt;--json&lt;/code&gt; flag.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;~/code/yarn-v2-workspace ❯ yarn workspaces list --json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;yarn-v2-workspace&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;packages/a&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;packages/b&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;parsing-json&quot;&gt;Parsing JSON&lt;/h3&gt;
&lt;p&gt;Yarn’s JSON output is pretty good but we don’t have a way to parse JSON natively in Lua-JIT.
Luckily, there’s an open source module called &lt;a href=&quot;https://github.com/rxi/json.lua&quot;&gt;json.lua&lt;/a&gt; that can do this for us.
If this were JavaScript, I would have probably gone ahead at this point and &lt;code&gt;npm install&lt;/code&gt;ed this package,
but this isn’t JavaScript.&lt;/p&gt;
&lt;p&gt;Most Neovim plugins focus on being self contained without any external dependencies that aren’t other Neovim plugins.
Lua does have a package manager called &lt;code&gt;luarocks&lt;/code&gt; but I don’t want consumers of this plugin to have to download additional dependencies.
So, I’m left with two choices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use the module as a git sub-module&lt;/li&gt;
&lt;li&gt;Copy and paste&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking at the commit history of &lt;a href=&quot;https://github.com/rxi/json.lua&quot;&gt;json.lua&lt;/a&gt;, there haven’t been any updates in about 2 years.
It might be better to use a git sub-module but I don’t think this repo will get any major updates any time soon.
The entire module is a single file so I decided to copy and paste it into my repo (keeping relevant license information and attributions).&lt;/p&gt;
&lt;h2 id=&quot;understanding-telescope&quot;&gt;Understanding Telescope&lt;/h2&gt;
&lt;p&gt;Whenever I start a new project or learn a new stack, I start by reading.
Today’s reading material happens to be the wonderful &lt;a href=&quot;https://github.com/nvim-telescope/telescope.nvim/blob/master/developers.md&quot;&gt;Telescope developer documentation&lt;/a&gt;.
I’ve found that reading docs and fully understanding how something works can save a lot of time, stack overlow searches, and struggles later on.&lt;/p&gt;
&lt;p&gt;Telescope’s API is fairly simple.
There are pickers out of which you can pick items.
Pickers contain a list of items supplied by a finder.
Pickers also do sorting which can be configured by a sorter.
When an item is selected, there is an event that triggers an action which does something with the chosen item.
Telescope comes with some good defaults for finders, sorters, and actions.
I didn’t have to deviate too much from these defaults.&lt;/p&gt;
&lt;p&gt;This is what I ended up with after story time (i.e. documentation reading time):&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;pickers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  prompt_title&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;Node Workspaces - &amp;quot; &lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.. &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;package_manager&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- simple default finder&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  finder&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;finders&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new_table&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    results&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;workspace_keys&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- simple default sorter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  sorter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;conf&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;generic_sorter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- a little more complexity in the action mapping&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- but it&amp;#39;s pretty much just taking the selected item&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- and looking up it&amp;#39;s directory from a map&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  attach_mappings&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;prompt_bufnr&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    actions&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;select_default&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;replace&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      actions&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;close&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;prompt_bufnr&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; selection&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;action_state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get_selected_entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;selection&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; dir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;workspaces&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.fs.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;normalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;workspace_root&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; .. &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/&amp;quot; &lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.. &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;dir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;      -- this is where we change the cwd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.api.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;nvim_set_current_dir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }):&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have something that works!
It does everything I initially set out to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only Yarn Berry Support&lt;/li&gt;
&lt;li&gt;Everything lives in my dotfiles&lt;/li&gt;
&lt;li&gt;No file preview is necessary, just a picker&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just pretend the screenshot doesn’t say ‘pnpm’. I forgot to take a picture at this point.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_basic_plugin.Ck2hK9Pr_Z1Sdjmp.webp&quot; alt=&quot;Basic Plugin&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;667&quot; class=&quot;image&quot;&gt; 
&lt;h2 id=&quot;building-out-compatability&quot;&gt;Building Out Compatability&lt;/h2&gt;
&lt;p&gt;With a feeling of accomplishment, I could either stop here or make the plugin more robust.
So I brought forward a new set of goals that had been the in back of my mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support for all major package managers (npm, yarn, pnpm, and yarn-berry)&lt;/li&gt;
&lt;li&gt;Extract the extension into its own repository for distribution&lt;/li&gt;
&lt;li&gt;Show a preview of the package’s &lt;code&gt;package.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;detecting-the-package-manager&quot;&gt;Detecting the Package Manager&lt;/h3&gt;
&lt;p&gt;First things first, we need to figure out what package manager is being used.
I had a general idea of how to detect a package manager, but Google helped me solidify my fuzziness.
A quick Google search for “How to detect JavaScript package manager” led me to the &lt;a href=&quot;https://www.npmjs.com/package/detect-package-manager&quot;&gt;detect-package-manager&lt;/a&gt; module.
Sure this thing is written in JavaScript but it’s already worked out all the logic I need.
And yep, &lt;a href=&quot;https://github.com/egoist/detect-package-manager/blob/07fb95c4de3e1cf72eadaf1c0fbf9ce0b93111a3/src/index.ts#L46-L50&quot;&gt;lines 46-50&lt;/a&gt; tell me everything I need to know:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    pathExists&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cwd&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;yarn.lock&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    pathExists&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cwd&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;package-lock.json&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    pathExists&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;resolve&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cwd&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;pnpm-lock.yaml&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  ]).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(([&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;isYarn&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;isNpm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;isPnpm&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And re-implementing that logic in Lua:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;--- detect which node package manager is being used&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;---&lt;/span&gt;&lt;span style=&quot;color:#C678DD;font-style:italic&quot;&gt;@param&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt; root_path string path to workspace root&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;---&lt;/span&gt;&lt;span style=&quot;color:#C678DD;font-style:italic&quot;&gt;@param&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt; package_json table parsed package.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;---&lt;/span&gt;&lt;span style=&quot;color:#C678DD;font-style:italic&quot;&gt;@return&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt; string one of &lt;/span&gt;&lt;span style=&quot;color:#98C379;font-style:italic&quot;&gt;&amp;#39;npm&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379;font-style:italic&quot;&gt;&amp;#39;yarn&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;or &lt;/span&gt;&lt;span style=&quot;color:#98C379;font-style:italic&quot;&gt;&amp;#39;pnpm&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; detect_package_manager&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;root_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;package_json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;npm&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; file_exists&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;root_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; .. &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/yarn.lock&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;yarn&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    elseif&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; file_exists&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;root_path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; .. &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/pnpm-lock.yaml&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;pnpm&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; == &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;yarn&amp;quot; &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        -- check packagejson for packageManager to see if yarn berry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        if&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;            package_json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.packageManager ~= &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;            and &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;package_json&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.packageManager[&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] ~= &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;            res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;yarn-berry&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; res&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;parsing-workspace-output&quot;&gt;Parsing Workspace Output&lt;/h3&gt;
&lt;p&gt;All of the major pacakage managers (npm, pnpm, yarn, and yarn-berry) all support workspaces,
but they all do workspace package listing a little differently.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# npm - gives proper JSON output&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# caveat: must be executed from the root of the workspace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -json&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -depth&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -omit=div&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# pnpm - gives proper JSON output&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; ls&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --json&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -r&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# yarn v1 - gives proper JSON output&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# caveat: must remove first and last lines of output for proper JSON&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;yarn&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; workspaces&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; info&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;# yarn-berry - gives proper JSON output on multiple lines&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;yarn&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; workspaces&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --json&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I won’t delve into the details, but I had to figure out ways to parse the different JSON
outputs for each package manager.&lt;/p&gt;
&lt;h2 id=&quot;using-a-previewer&quot;&gt;Using a Previewer&lt;/h2&gt;
&lt;p&gt;We still have yet to cover the telescope previewer.
This is the thing that shows a preview of the item when your cursor is over an item.
For example, for file pickers, it will show a preview of the file.&lt;/p&gt;
&lt;p&gt;I was stumped for a bit on how to get a previewer to work but I remembered to turn back to the trusty documentation.
And there it is, with the &lt;a href=&quot;https://github.com/nvim-telescope/telescope.nvim/blob/master/developers.md#entry-maker&quot;&gt;&lt;code&gt;entry_maker&lt;/code&gt;&lt;/a&gt;
field. With &lt;code&gt;entry-maker&lt;/code&gt; we can continue to use the default file previewer to show the selected package’s &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Putting it all together, along with a few refactors:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;pickers&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  prompt_title&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;Node Workspaces - &amp;quot; &lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.. &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;package_manager&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- default finder using `entry_maker`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  finder&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;finders&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;new_table&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    results&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;workspaces&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    entry_maker&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;        -- add the path field for the previewer to read&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.fs.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;normalize&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;] .. &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/package.json&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        value&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        display&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        ordinal&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- default sorter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  sorter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;conf&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;generic_sorter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- default file previwer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  previewer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;conf&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;file_previewer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  -- mapping to change our cwd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;  attach_mappings&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;prompt_bufnr&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ABB2BF;font-style:italic&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    actions&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;select_default&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;replace&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      actions&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;close&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;prompt_bufnr&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      local&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; selection&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;action_state&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;get_selected_entry&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;      vim&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.api.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;nvim_set_current_dir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;selection&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.value)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we’re still just using a lot of Telescope’s default configurations.
The end result looks something like this:&lt;/p&gt;
&lt;img src=&quot;/_astro/01_final_plugin.CmxwmqgL_ZsSirW.webp&quot; alt=&quot;Final Plugin&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;841&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Overall, Telescope.nvim has a really well built API.
The default finder, sorter, and previewers were more than enough for this extension.
The documentation is well written and easy to follow.
Anything not included in the guide can be found in the &lt;code&gt;:help&lt;/code&gt; docs or by using other
extensions as examples.&lt;/p&gt;
&lt;p&gt;I didn’t allow myself to get caught up with lofty goals and focused on getting a quick win.
I used open source code to both save time and avoid re-inventing the wheel.
Then I came back to flesh out my ideas and add greater usability.&lt;/p&gt;
&lt;p&gt;You can see the completed plugins source code on &lt;a href=&quot;https://github.com/enochchau/telescope-node-workspace.nvim&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id=&quot;update&quot;&gt;&lt;em&gt;Update&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;9/5/2022&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I’ve discovered that Neovim has &lt;a href=&quot;https://neovim.io/doc/user/builtin.html&quot;&gt;builtin&lt;/a&gt; JSON serialization.
I’ve removed &lt;a href=&quot;https://github.com/rxi/json.lua&quot;&gt;json.lua&lt;/a&gt; and replaced it with this builtin function.
It pays to read the docs!&lt;/p&gt;</description><pubDate>Tue, 30 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Counting is Hard (Tangerine Amigurumi)</title><link>https://enochchau.com/blog/2022/counting-is-hard/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/counting-is-hard/</guid><description>&lt;p&gt;“Clara’s got me in the sweat shop, trying to learn to crochet.”&lt;/p&gt;
&lt;img src=&quot;/_astro/00_wasted_yarn.CcFLqTuQ_26z7n0.webp&quot; alt=&quot;wasted yarn&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;These were the musings of a deranged boy at 3AM after having wasted a whole pile of yarn.&lt;/p&gt;
&lt;p&gt;I’ve been learning to crochet.
My mom taught me how to knit and sew when I was a kid but I never got to learn to crochet.
Well mom, now I’m grown up, and YouTube exists.&lt;/p&gt;
&lt;p&gt;My first experience with crocheting was not good.
Clara tried to teach me the basics but I just could not figure it out that day.
But it was helpful to have a real person answer my initial questions.
And it was helpful to have seen an example in person.
And it was helpful to have my hands held.&lt;/p&gt;
&lt;p&gt;To make a tangerine amigurumi, you start with a 6 stitch magic circle.
As you continue to stitch around the circle, you increase it’s circumference gradually until you have a hemisphere.
At which point, you begin to decrease your stitch count until you complete the sphere.&lt;/p&gt;
&lt;p&gt;The day after my 3AM night, after a fair number of YouTube videos, I was finally starting to get the hang of it.
I made at least 10 magic circles before I settled on one that I thought was good.&lt;/p&gt;
&lt;img src=&quot;/_astro/01_magic_circle.FVGZpBYI_Z1mWdkr.webp&quot; alt=&quot;Magic Circle&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;But counting is hard so I pretty much messed up the next layer immediately.
The neat thing is, it doesn’t matter if I mess up.
I can just keep going and try to fix it in the next layer.
Or just keep fucking it up and end up with a lumpy tangerine.&lt;/p&gt;
&lt;p&gt;One tip is to put a bobby pin at the starting stitch for that layer.
That way, you don’t have to count your stitches as you go around.
I don’t have any bobby pins, but I do have some twist ties.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_count_is_hard.DShu-fVe_Z1Y1m2u.webp&quot; alt=&quot;Counting is Hard&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I got to the point where I needed stuffing so I had to pause for a day.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_in_need_of_stuffing.C3ntZGTF_Z2i6kwD.webp&quot; alt=&quot;In need of stuffing&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;In the meantime, I made the leaf.
The leaf is a combination of chain stitches, singles crochets, and half double crochets.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_leaf.Cs9pPkb3_1LI3Qj.webp&quot; alt=&quot;Leaf&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Stuffing acquired.
After my initial 3AM night, I did most of my crocheting on Bart.
Crocheting is a good commute time killer.
Usually I would read &lt;a href=&quot;https://news.ycombinator.com/&quot;&gt;HN&lt;/a&gt; or
&lt;a href=&quot;https://open.spotify.com/episode/65KBuktfEh9PGD1KVALxD3?si=00725a5ad6be4127&quot;&gt;listen&lt;/a&gt;
&lt;a href=&quot;https://open.spotify.com/episode/02zHHYpJdaFBDpjyiNCzR3?si=3ce4943a5dc14428&quot;&gt;to&lt;/a&gt;
&lt;a href=&quot;https://open.spotify.com/episode/3LddnZjkpflldHXnRZ0rrw?si=d25d0dfbdfbe43e9&quot;&gt;podcasts&lt;/a&gt;
but then I tend to stare at people since I have nothing to do with my eyes and hands.&lt;/p&gt;
&lt;img src=&quot;/_astro/05_stuffed.EAFeWPFb_Z1wFFYM.webp&quot; alt=&quot;Stuffed&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;With the tangerine stuffed, I finished up the sphere.
It’s pretty lumpy.&lt;/p&gt;
&lt;p&gt;Now I need a needle to finish up threading the tangerine.&lt;/p&gt;
&lt;img src=&quot;/_astro/06_in_need_of_needle.COeXxAw-_1kY7nf.webp&quot; alt=&quot;In need of a needle&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Yarn needles do exist. They’re really just bigger versions of regular needles.
But like the bobby pins, I don’t have any yarn needles.
I do have a large sewing needle though.
The bible says something about threading a camel through the eye of a needle.
I guess Jesus never tried threading yarn through a sewing needle.&lt;/p&gt;
&lt;img src=&quot;/_astro/07_bound.D-Z6pNTo_2wtxBK.webp&quot; alt=&quot;Tangerine bound in yarn&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;And we’re all done!
In all it’s lumpy glory.
&lt;a href=&quot;https://www.joannwphotos.com/&quot;&gt;Joann&lt;/a&gt; thought it was a pumpkin but I’ll consider that a compliment for my first amigurumi.&lt;/p&gt;
&lt;img src=&quot;/_astro/08_tangerine._kmMSF7r_l2adg.webp&quot; alt=&quot;Tangerine&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;If you’re interested in making one, I got the pattern (from Clara who got it) from this &lt;a href=&quot;https://youtu.be/BxqiN7F4PUY&quot;&gt;YouTube video&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I want to make a hat next.
Specifically, I want to make this hat.&lt;/p&gt;
&lt;img src=&quot;/_astro/09_loved_hat.7-ta4weT_Z1R2ciI.webp&quot; alt=&quot;Loved hat&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Don’t I look so good in that hat?
I loved that hat.
But unfortunately, that’s not my hat.
So I’m gonna make my own hat.
I hope it’ll be just as good as that hat.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(P.S. The mice you see in the background of the photos are a Razer Viper Mini and a Logitech M510.
The Razer Viper Mini is my FPS mouse.
It’s around 60g and I use a claw grip.
I think I got up to Plat in Valorant season 2 with that mouse but I haven’t been focused on gaming lately.
The Logitech M510 I’ve had forever.
I used to use it for work but I’ve recently switched to using the &lt;a href=&quot;/blog/2022/ploopy-classic-build&quot;&gt;Ploopy Classic&lt;/a&gt;.
)&lt;/em&gt;&lt;/p&gt;</description><pubDate>Fri, 26 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Au Revior Benjamin</title><link>https://enochchau.com/blog/2022/au-revior-benjamin/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/au-revior-benjamin/</guid><description>&lt;p&gt;I had another 3AM night yesterday, staying up writing, so I started the
day strong with a trip to Coffee Bar.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_coffee_bar.Crn67_tC_ZUMlWu.webp&quot; alt=&quot;Coffee Bar&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;This is a new cafe for me but it’s been on my radar.
I like to go on Google Maps and trawl around the flat pastel shapes looking for
spots that might be good.
Coffee Bar is good!
One of the better cappuccinos I’ve had.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_coffee_cup.Cir9il8-_JYsWH.webp&quot; alt=&quot;Coffee Bar Coffee&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Behind the counter were sitting a couple of liter mason jars filled with liquid and what looked like vanilla pods.
So I was like:&lt;/p&gt;
&lt;p&gt;“What’s in those jars?”&lt;/p&gt;
&lt;p&gt;And the barista, he was like:&lt;/p&gt;
&lt;p&gt;“It’s vanilla syrup, we make our own syrup!”&lt;/p&gt;
&lt;p&gt;He said it takes about three days to steep the pods in a simple syrup.
Afterwards, he’ll reuse one pod for the next batch and add two new pods.
The used pods can be used for baking and something about creme brulee,
I couldn’t really make out the last bit behind his mask.
I’ll be back to try a vanilla drink next time.&lt;/p&gt;
&lt;img src=&quot;/_astro/01_inside_coffee_bar.CGeR8XjY_ZBXAFK.webp&quot; alt=&quot;Inside Coffee Bar&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I like to share at least one picture of the interior of the buildings I go in.
When you play open world games, you get to run/swing/fly around the city but you never get to go inside any of the buildings.
It’s the same thing in real life, half the buildings I can’t go into because they’re not public buildings.
When I finally get to enter, it’s like a Christmas morning surprise.&lt;/p&gt;
&lt;p&gt;There’s a sense of community inside buildings.
Everyone is there to do something similar.
For example, if it’s a coffee shop, everyone is there to get coffee.
There’s a certain, albeit brief, sense of camaraderie to us all having the same purpose.
Standing together in line, in close proximity.&lt;/p&gt;
&lt;p&gt;Further along my walk I came across a mysterious set of stairs.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_mystery_stairs.D9WEkLP7_2855MS.webp&quot; alt=&quot;Mystery Stairs&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Which lead to…&lt;/p&gt;
&lt;img src=&quot;/_astro/04_courtyard_park.aRJ9XXle_Z1Uf7rb.webp&quot; alt=&quot;Couryard park&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;A courtyard park surrounded by towering office buildings.
I love the bits of greenery interspersed around the concrete jungle.&lt;/p&gt;
&lt;p&gt;I’m starting to notice that there seems to be an interconnected set of walkways between the buildings in this part of town.
One of these days, I’m going to have to see how far I can go between these bridges.
I might be blocked short by a locked door or two but that will be part of the adventure.&lt;/p&gt;
&lt;img src=&quot;/_astro/05_sky_brdige.DWSXwdgZ_h7qmX.webp&quot; alt=&quot;sky bridge&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;hr/&gt;
&lt;h2 id=&quot;benjamin&quot;&gt;Benjamin&lt;/h2&gt;
&lt;p&gt;Today is Ben’s last day at &lt;a href=&quot;https://gatsby.events&quot;&gt;Gatsby&lt;/a&gt;, so we had a little celebration at High Horse.&lt;/p&gt;
&lt;img src=&quot;/_astro/07_horse_pole.Bx35A9BN_1QtDW4.webp&quot; alt=&quot;Horse Pole&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/06_high_horse.3lHiG-y3_1FXaj8.webp&quot; alt=&quot;High Horse&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;611&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Ben was my intern this summer.
I’m really proud of what he’s been able to accomplish.
I feel like he’s learned so much and grown so much as an engineer.
I loved his curiosity.
At times, he would challenge my own understanding by confronting my assumptions.
I learned as much from him as he did from me.&lt;/p&gt;
&lt;h2 id=&quot;mentorship&quot;&gt;Mentorship&lt;/h2&gt;
&lt;p&gt;I’ve always romanticized mentorship.
I had a mentor and was a mentor multiple times in University,
but it’s never really lived up to my expectations.&lt;/p&gt;
&lt;p&gt;Mentorship is difficult to get right.
Both parties have to be equally invested in making the most of the mentorship.
The hard part of mentorship is building the initial trust.
The mentee has to feel comfortable coming to the mentor for help.
In my experience, building this trust has to be initiated by the mentor.
But slowly, as the mentee becomes comfortable, the roles will reverse and the mentee will become more proactive in reaching out.
In Ben’s case, I still think I could have done better, spent more time with him, worked more closely with him.&lt;/p&gt;
&lt;p&gt;I’ll miss seeing you around.&lt;/p&gt;
&lt;p&gt;Au Revior Benjamin.&lt;/p&gt;
&lt;div data-astro-cid-aomsn35f&gt; &lt;iframe src=&quot;https://www.youtube.com/embed/GwJ9Itk2oMQ&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen class=&quot;astro-aomsn35f&quot; data-astro-cid-aomsn35f&gt;&lt;/iframe&gt; &lt;/div&gt; </description><pubDate>Thu, 25 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Terminal History</title><link>https://enochchau.com/blog/2022/terminal-history/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/terminal-history/</guid><description>&lt;p&gt;I’m a terminal enthusiast.
At this point, I’ve got a pretty nifty &lt;a href=&quot;https://github.com/enochchau/dotfiles&quot;&gt;shell setup&lt;/a&gt;.
My workflow revolves around the command line, &lt;a href=&quot;https://github.com/tmux/tmux&quot;&gt;tmux&lt;/a&gt;, and &lt;a href=&quot;https://neovim.io/&quot;&gt;Neovim&lt;/a&gt;.
And I love it.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_wsl_today.7EawtKRZ_Z1Of3V2.webp&quot; alt=&quot;wsl, nvim, tmux&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;523&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;But it wasn’t always this way.
My &lt;a href=&quot;https://github.com/enochchau/dotfiles&quot;&gt;dotfiles&lt;/a&gt; have been a work in progress since I was first introduced to the terminal in University.&lt;/p&gt;
&lt;h2 id=&quot;intro-to-c&quot;&gt;Intro to C&lt;/h2&gt;
&lt;p&gt;I studied electrical engineering at UC Irvine.
I get the feeling that non technical people think EE and CS are closely related but in practice this is far from the truth.
Overall, I took just 2 programming courses over my entire four years (not counting Hardware Description Languages like Systems Verilog or VHDL).&lt;/p&gt;
&lt;p&gt;Every engineering student at UC Irvine must take an introductory programming course.
Mine was EECS10: Intro to C.
On our first day, our professor gave us two choices:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.codeblocks.org/&quot;&gt;Code Blocks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.vim.org/&quot;&gt;Vim&lt;/a&gt; on the &lt;a href=&quot;http://eecs.uci.edu/&quot;&gt;University Servers&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I looked briefly at Code Blocks but decided against it.
IDE’s are a bit intimidating for newbies.
Too many bells and whistles.
I don’t need bells and whistles to confuse me when I don’t know what’s going on in the first place.&lt;/p&gt;
&lt;p&gt;So SSH and Vim it is.
Being on Windows, I used PuTTY.&lt;/p&gt;
&lt;img src=&quot;/_astro/01_putty.Ct_8RC9s_Z1nQGcG.webp&quot; alt=&quot;putty&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;782&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;And coded up my first assignment.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_first_assignment_in_vim.CiREaQ9h_Z2ejJ35.webp&quot; alt=&quot;first assignment in vim&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;425&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;It really doesn’t get any more simple than a ‘hello world’.&lt;/p&gt;
&lt;p&gt;Today, I logged into the servers for the first time in a long time and this was my vimrc (Vim config file).
But I’m pretty sure I didn’t know what a vimrc was at this point, or at any point during these early years.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_uci_server.Dtn26iHU_Z1WLaNf.webp&quot; alt=&quot;uci server terminal session&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;539&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I was navigating in Vim using the arrow keys.
I didn’t even know you could open more than one file at a time.
Looking back, I have no idea how I finished these assignments without a linter and LSP telling me what to do.
Modern development tools have really spoiled me.&lt;/p&gt;
&lt;p&gt;Eventually, I discovered &lt;a href=&quot;https://mobaxterm.mobatek.net/&quot;&gt;MobaXTerm&lt;/a&gt; and used that in place of PuTTY.
MobaXTerm has a built in FTP client so I switched from Vim to &lt;a href=&quot;https://atom.io/&quot;&gt;Atom&lt;/a&gt; and uploaded my assignments using FTP.&lt;/p&gt;
&lt;p&gt;Atom and the &lt;a href=&quot;https://www.arduino.cc/en/software&quot;&gt;Arduino IDE&lt;/a&gt; (god bless that abomination)
were my preferred text editors for the rest of University, but eventually I came back to Vim.&lt;/p&gt;
&lt;h2 id=&quot;coffee-grams&quot;&gt;Coffee Grams&lt;/h2&gt;
&lt;p&gt;During the height of the pandemic in 2020, I had moved back home and needed desperately for something to fill my time.
I decided I wanted to learn web development with the LAMP stack.
Well, actually, I thought the only language you could use for servers was PHP.
PHP is a good language for learning web development, but to be fair, I wouldn’t want to write it anymore today.&lt;/p&gt;
&lt;p&gt;Having just returned from Spring break in Seattle, I was really getting into coffee.
I wanted a web app that could track and share my &lt;a href=&quot;/blog/2020/coffeegrams/&quot;&gt;coffee gram&lt;/a&gt; ratios and tasting notes.&lt;/p&gt;
&lt;p&gt;All the LAMP stack web dev guides were for Linux (sorry, GNU/Linux) but I wasn’t exactly thrilled to use Desktop Linux at this point in life.
With a couple of old desktops sitting unused at home, I figured, why not run my own home server.
So that’s exactly what I did.
I setup an Ubuntu server in the adjacent room and would SSH into it to do web development.
And with SSH comes Vim.&lt;/p&gt;
&lt;p&gt;So I was back to Vim.&lt;/p&gt;
&lt;p&gt;What really inspired me to get good at Vim were some
&lt;a href=&quot;https://youtu.be/gZCXaF-Lmco&quot;&gt;older videos&lt;/a&gt; by &lt;a href=&quot;https://www.youtube.com/c/ChrisAtMachine&quot;&gt;chris@machine&lt;/a&gt;
where he turns Neovim into a more usable editor.&lt;/p&gt;
&lt;p&gt;I drilled myself to learn the proper key binds.
Gone were the days of arrow key navigation.
Hello hjkl.
Every day, I’d pick a new key binding from the &lt;a href=&quot;https://vim.rtorr.com/&quot;&gt;cheatsheet&lt;/a&gt; and try to use it as much as possible.
After a few weeks, I’d learned most of the important navigation and editing techniques.
That’s not to say I became a master.
I still learn new things about Vim to this day.&lt;/p&gt;
&lt;h2 id=&quot;the-college-dropout-and-software-engineering&quot;&gt;The College &lt;a href=&quot;https://github.com/enochchau/dropout-numpad&quot;&gt;Dropout&lt;/a&gt; and Software Engineering&lt;/h2&gt;
&lt;p&gt;After my undergrad, I was slated to go to graduate school at UC Irvine for a
&lt;a href=&quot;https://mecps.uci.edu/&quot;&gt;Masters in Embedded and Cyper-physical Systems&lt;/a&gt;.
I did go to graduate school for about a quarter.
But god damn I hated that shit.&lt;/p&gt;
&lt;iframe style=&quot;border-radius:12px&quot; src=&quot;https://open.spotify.com/embed/album/4Uv86qWpGTxf7fU7lG5X6F?utm_source=generator&quot; width=&quot;100%&quot; height=&quot;80&quot; frameBorder=&quot;0&quot; allow=&quot;autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Yeah, that’s me.
The college dropout.&lt;/p&gt;
&lt;p&gt;Closing one door inevitably leads to getting lost in a maze.
I dunno, this isn’t supposed to a good metaphor.
This was not a happy time in my life.&lt;/p&gt;
&lt;p&gt;But one thing I knew: I love to code.
So let’s code some things.&lt;/p&gt;
&lt;p&gt;My brother was in the tail end of a coding bootcamp so I had someone who could guide me through some of the basics.
YouTube filled me in on the rest.&lt;/p&gt;
&lt;p&gt;Web development is easy to learn.
Not that the material is easy, but there’s just so many resources online.
There’s like five thousand tutorials on how to setup an Express app.
If you don’t like the sound of this dudes voice, well, there’s like 5 other dudes with tutorials just as good.&lt;/p&gt;
&lt;p&gt;I finally found out about &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/wsl/about&quot;&gt;WSL&lt;/a&gt; and that was a game changer.
No longer would I have to spin up my own home server or use Desktop Linux.&lt;/p&gt;
&lt;p&gt;I was still using the terminal but I had switched off Neovim to &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VSCode&lt;/a&gt;.
I did toy around with &lt;a href=&quot;https://github.com/neoclide/coc.nvim&quot;&gt;CoC&lt;/a&gt; for a bit but ultimately, it was just too challenging.
It’s hard to setup Neovim to the same level of productivity that you get with VSCode for free.&lt;/p&gt;
&lt;p&gt;VSCode was my IDE for awhile. Of course, I used the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vscodevim.vim&quot;&gt;Vim extension&lt;/a&gt; but any &lt;em&gt;real terminal enthusiast&lt;/em&gt; will tell you that emulation just isn’t the same as the real thing.&lt;/p&gt;
&lt;h2 id=&quot;vscode&quot;&gt;VSCode&lt;/h2&gt;
&lt;p&gt;Unfortunately, I have to briefly mention VSCode because that was my terminal and IDE for awhile.
I used VSCode’s embedded terminal all through my self taught bootcamp and into my first job as a Software Engineer.&lt;/p&gt;
&lt;p&gt;VSCode is a massively innovative piece of open source software.
Protocols such as &lt;a href=&quot;https://microsoft.github.io/language-server-protocol/&quot;&gt;LSP&lt;/a&gt; and
&lt;a href=&quot;https://microsoft.github.io/debug-adapter-protocol/&quot;&gt;DAP&lt;/a&gt; both originated from VSCode and were later
turned into open standards that were ported to other editors.
Additionally, CoC, Neovim’s earliest language server implementation, was essentially a bridge between VSCode extensions and Vim.
While I don’t use VSCode anymore, I still appreciate what it’s done for Editors and the JavaScript community.&lt;/p&gt;
&lt;p&gt;Who would have thought Microsoft would ever embrace open source as it has today.&lt;/p&gt;
&lt;div data-astro-cid-aomsn35f&gt; &lt;iframe src=&quot;https://www.youtube.com/embed/Vhh_GeBPOhs&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen class=&quot;astro-aomsn35f&quot; data-astro-cid-aomsn35f&gt;&lt;/iframe&gt; &lt;/div&gt; 
&lt;h2 id=&quot;javascript-is-slow&quot;&gt;JavaScript is Slow&lt;/h2&gt;
&lt;p&gt;When I started at &lt;a href=&quot;https://gatsby.events&quot;&gt;Gatsby&lt;/a&gt;, I was given a 13inch i5 MacBook.
Well, I wasn’t really given it.
More like MZ had left it with Nate when he moved and
I just happened to be able to pick it up that day instead of waiting a week for a different machine.
That mother fucker MZ took the charger with him too.
And of course they were sold out at the Apple store.&lt;/p&gt;
&lt;p&gt;I had to wait a full 3 days without a charger for Amazon to send me one.
I used a fucking 12W phone charger for 3 days.
Do you know how fucking slow a 12W charger is??&lt;/p&gt;
&lt;p&gt;Okay anyways.&lt;/p&gt;
&lt;p&gt;I used this i5 laptop for awhile but eventually, VSCode was just too slow.
Running 5 Chrome tabs, VSCode, 10 NodeJS services, Mongo, and Redis locally was just too much for this little machine.
By the way, that’s like 16+ instances of &lt;a href=&quot;https://v8.dev/&quot;&gt;V8&lt;/a&gt; munching on my RAM.&lt;/p&gt;
&lt;p&gt;So I bit the bullet.
I doubled down on iTerm2, Neovim, and CoC and it had never felt better to type
on my NK Silk Black switches lubed with Krytox 205g0 (if you know, you know).
I used this setup for awhile until Neovim 0.5 dropped and the hype shifted to the native LSP client.&lt;/p&gt;
&lt;h2 id=&quot;the-present&quot;&gt;The Present&lt;/h2&gt;
&lt;p&gt;I am a hype beast.
Where the hype goes I follow.
When the hype is to use Lua plugins for Neovim, I use Lua plugins.
When the hype is to use the native LSP client for Neovim, I use the native LSP client.&lt;/p&gt;
&lt;p&gt;Is your terminal slow?
Well hop on the hype train to &lt;a href=&quot;https://alacritty.org/&quot;&gt;Alacritty&lt;/a&gt; town where we got the fastest terminals on this side of the Mississippi.&lt;/p&gt;
&lt;p&gt;Oh fuck, Alacritty doesn’t have tabs or splits.
Yeah… that’s basically how I ended up using tmux.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_tmux_macos.P2kGwQ0I_Z1rB8Jm.webp&quot; alt=&quot;Tmux on MacOS&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;489&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;That brings us to today.
I got a &lt;a href=&quot;https://www.youtube.com/watch?v=xt5ghXdq6Z0&quot;&gt;smanging&lt;/a&gt; setup that’s fast, keyboard centric, and a joy to use.&lt;/p&gt;
&lt;p&gt;Sometimes I forget about the joy of programming but everyday I get to tinker with my
&lt;a href=&quot;https://github.com/enochchau/dotfiles&quot;&gt;dotfiles&lt;/a&gt; is another day I remember why I love computers so much.
Stay alive in the TTY.&lt;/p&gt;</description><pubDate>Thu, 25 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Silent Hill and 我的好朋友</title><link>https://enochchau.com/blog/2022/silent-hill-and-good-friends/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/silent-hill-and-good-friends/</guid><description>&lt;p&gt;Today, we start the day at the Lord’s table.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_hgsb.BgpjJvBN_2n2w9z.webp&quot; alt=&quot;House of God in San Bruno&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I grew up at House of God in San Bruno.
Most of the people at this Church have known me since I was born.&lt;/p&gt;
&lt;p&gt;In this day and age, many people have varying views on religion.
Some people think it’s culty, other people think our country is going to shit for lack there of.
Religion to me is both spirituality and community.
Being Christian is something I can have in common with any other Christian even if I have nothing else in common with them.
There’s an instant bond and sense of community basically out of nothing.
You can also never stop being a Christian.
If I got injured, I wouldn’t be a climber anymore until I recovered.
Nothing really stops you from being Christian.
Always having a community to fall back is great for mental health.
I don’t really care what you practice or if you practice at all.
That’s my view of religion.&lt;/p&gt;
&lt;p&gt;My church is located right in down town San Bruno.
It’s not much of a down town, just a single street, but it’s my hometown downtown.&lt;/p&gt;
&lt;img src=&quot;/_astro/01_laundromat.DxQJ1Hbb_Z1Ptine.webp&quot; alt=&quot;Laundromat&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Little known fact: parts of Silent Hill 2
&lt;a href=&quot;http://www.igotaletter.com/rarities/oldneelystreet/&quot;&gt;were modeled&lt;/a&gt;
after San Mateo Ave in San Bruno.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_new_well.Cz7LZRuA_25bilb.webp&quot; alt=&quot;New Well&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;So I basically grew up and go to church next to a video game.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;“Would you like to try our new tortillas?”&lt;/p&gt;
&lt;p&gt;“Are they gluten free?”&lt;/p&gt;
&lt;p&gt;“No, they’re made of wheat.”&lt;/p&gt;
&lt;h2 id=&quot;新朋友&quot;&gt;新朋友&lt;/h2&gt;
&lt;p&gt;Today, I’m having lunch with a new friend, in Palo Alto, at Izzy’s Brooklyn Bagels.&lt;/p&gt;
&lt;img src=&quot;/_astro/06_izzys_bagels.CbF12daM_Z23mkKe.webp&quot; alt=&quot;Izzy&apos;s Brooklyn Bagels&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The bagels are good.
I’m not a huge fan of bagel sandwiches but these were pretty darn tasty.
My go to for bagels is usually a blueberry bagel with plain cream cheese.
But today, I skipped breakfast so I needed the calories.
I did get a blueberry bagel today as ‘dessert’ but sans cream cheese.
Can’t have too many calories.&lt;/p&gt;
&lt;p&gt;Clara planned well since there was also a farmer’s market going on right at that very street.
Farmer’s markets remind me slightly of Costco.
Most of the stalls have some sort of free sample for you to try.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_cauliflower.Bx5Q8USc_Z1XmJqJ.webp&quot; alt=&quot;Cauliflower&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Cauliflower reminds me of tripping on acid.
The pattern on the florets is vaguely similar to the geometric visuals of psychedelics.&lt;/p&gt;
&lt;img src=&quot;/_astro/05_zombie_coffee.BPaGBG7M_Zn2abH.webp&quot; alt=&quot;Zombie Coffee&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Can’t forget about my daily ritual.
Today’s cup was a nitro cold brew from Zombie Coffee.
I’m enamored with neon signs albeit this one was about as plain as they come.
By the way, you can see my reflection in the photo.&lt;/p&gt;
&lt;h3 id=&quot;iced-coffee&quot;&gt;Iced Coffee&lt;/h3&gt;
&lt;p&gt;I rarely drink iced coffee.
First, it’s hard to make at home since I rarely keep ice cubes.
Second, I usually keep my caffeine consumption to the morning.
In the early hours, I’d much rather wake up to a hot beverage than a cold one.&lt;/p&gt;
&lt;p&gt;Nevertheless, I can distinctly remember two other times I’ve had iced coffee.&lt;/p&gt;
&lt;h4 id=&quot;rostar&quot;&gt;Rostar&lt;/h4&gt;
&lt;p&gt;The first is the summer of 2018 when I went to Tokyo.
We stopped by Rostar after a trip to an Onsen in Odaiba.
I heard about Rostar through a &lt;a href=&quot;https://youtu.be/qIbbJV-grHg?t=38&quot;&gt;Buzz Feed Worth It video on Coffee&lt;/a&gt;.
I can verify, Rostar’s iced coffee is really good for ¥100.&lt;/p&gt;
&lt;p&gt;Now I’m nostalgic… 2017 - 2019 really did have some good YouTube content.&lt;/p&gt;
&lt;h4 id=&quot;永康街&quot;&gt;永康街&lt;/h4&gt;
&lt;p&gt;The other one was somewhere near Yong Kang Jie in Taipei.
I visited in the summer of 2017 with my brother.
My mom had business around the area so my brother and I tagged along to hang out.
Soon enough, we realized we needed to find an escape from the sweltering, humid summer heat.
We found shelter in a little coffee shop tucked away one of the side streets.
They had cold brews and other coffee drinks made from elaborate brew towers.&lt;/p&gt;
&lt;p&gt;Taiwan is a bit famous for these
&lt;a href=&quot;https://yama-glass.com/products/yama-cold-drip-tower-6-8-cup-bamboo-straight-frame-32oz&quot;&gt;brew towers&lt;/a&gt;.
Ice goes in the top and as it melts, it brews coffee.
I’ve only ever seen these cold brew machines in Taiwan and at
&lt;a href=&quot;https://www.nevertoolattecafe.com/&quot;&gt;Never too Latte&lt;/a&gt; (in San Bruno, on San Mateo Ave!).
I guess you gotta come up with some innovative ways to make good cold coffee when it’s boiling outside half the year.&lt;/p&gt;
&lt;p&gt;We ordered in Chinese but somehow the waitress knew we were American.
I asked her how she knew and she said it was the way we dressed.
I guess that’s fair.
At University, I could usually tell who the international Chinese students were by the way they dressed.&lt;/p&gt;
&lt;p&gt;Unfortunately, I can’t remember what this coffee shop was called.
There’s a plethora of coffee shops in this area on Google Maps so it’s hard to pin down which one it was.
For all I know, it may not exist anymore after the pandemic.&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;好朋友們&quot;&gt;好朋友們&lt;/h2&gt;
&lt;p&gt;Today, we’re having Korean food for dinner, with some old friends, in San Francisco.&lt;/p&gt;
&lt;img src=&quot;/_astro/09_han_il_kwan_sign.DyqIiUaQ_Z5tS7q.webp&quot; alt=&quot;Han IL Kwan Sign&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;We got dinner set A, a meal for 6, but our table only had 5 people.
Spoiler, it was a lot of food.&lt;/p&gt;
&lt;p&gt;The server asked us if we wanted to have our meat cooked in the kitchen or if we wanted cook it ourselves on the grill.
Without much thought, I asked if we could cook it ourselves.&lt;/p&gt;
&lt;img src=&quot;/_astro/11_dinner.BuFIzZs6_Z8h2eO.webp&quot; alt=&quot;Korean food for Dinner&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I haven’t done KBBQ style self cooking since I left Irvine.
There’s not many KBBQ style restaurants up in my part of the Bay Area.&lt;/p&gt;
&lt;h3 id=&quot;red-castle&quot;&gt;Red Castle&lt;/h3&gt;
&lt;p&gt;Back in Irvine, we used to go to this place called &lt;a href=&quot;https://www.yelp.com/biz/red-castle-garden-grove-6&quot;&gt;Red Castle&lt;/a&gt;.
In terms of quality, it was pretty mid, but in terms of value, oh man.
It was like less than ~$20 for all you can eat meats.
For a bunch of half starved college students, that’s basically meat heaven.
I always left feeling disgusted but I have fond memories of the place.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Today was a long day but it was fun.&lt;/p&gt;
&lt;p&gt;I met a new friend.
I saw some old friends.&lt;/p&gt;
&lt;p&gt;I might not be writing for the next few days.
I’m planning on working from home so I may not do any walking.
But maybe I can get something in.
It’s our interns’ last week so I might have a couple days in the city to see them before they leave.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(P.S. I don’t have the Chinese keyboard enabled on this computer,
so I was dead ass just typing the English translations into Google translate and copying the resulting Chinese.)&lt;/em&gt;&lt;/p&gt;</description><pubDate>Sun, 21 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Ploopy Classic Build</title><link>https://enochchau.com/blog/2022/ploopy-classic-build/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/ploopy-classic-build/</guid><description>&lt;p&gt;Today we’re building the &lt;a href=&quot;https://github.com/ploopyco/classic-trackball&quot;&gt;Ploopy Classic&lt;/a&gt;.
I’ve been following &lt;a href=&quot;https://ploopy.co/&quot;&gt;Ploopy&lt;/a&gt; for a bit now.
I first found out about them through one of the mechanical keyboard subreddits.
All their mice use &lt;a href=&quot;https://github.com/qmk/qmk_firmware&quot;&gt;QMK&lt;/a&gt; which is a popular firmware for hobby mechanical keyboards.&lt;/p&gt;
&lt;p&gt;My wrist has been hurting lately.
A combination of long hours at the computer mixed with strenuous climbing
will do that to you.
Well, I’m not going to stop climbing so I might as well get a more ergonomic mouse.&lt;/p&gt;
&lt;p&gt;The kit I got requires some assembly.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_parts.Bv0f4ubA_1H9izS.webp&quot; alt=&quot;parts&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;In college, I worked at a research lab where I did my fair &lt;a href=&quot;/blog/2019/3phase&quot;&gt;share&lt;/a&gt; of &lt;a href=&quot;/blog/2020/wcb&quot;&gt;electronics&lt;/a&gt; &lt;a href=&quot;/blog/2020/projector-buddy&quot;&gt;assembly&lt;/a&gt;
so I’m comfortable with soldering.
Did I also mention I’ve &lt;a href=&quot;/blog/2020/macro-pad&quot;&gt;made&lt;/a&gt; a &lt;a href=&quot;/blog/2021/gis&quot;&gt;few&lt;/a&gt; of my own &lt;a href=&quot;/blog/2020/fk-2001&quot;&gt;keyboards&lt;/a&gt;?&lt;/p&gt;
&lt;img src=&quot;/_astro/01_pcb.CoKMy-dK_mvxIT.webp&quot; alt=&quot;pcb&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The PCB setup in the base is looking good.
The grey plastic piece is the scroll wheel holder.
Initially, I had a bit of trouble with the press fit because I didn’t press it into the PCB hard enough.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_pcb_in_base.CRYTojRT_24ItPm.webp&quot; alt=&quot;pcb in base&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Side buttons in place.
For some reason, I got two sets of side buttons.
Must have been a shipping mistake on their end.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_side_buttons.C74_z_Zt_1GNNNm.webp&quot; alt=&quot;side buttons&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The scroll wheel is smooth and not stepped.
I liked stepped wheels.
I’ll probably end up using the
&lt;a href=&quot;https://github.com/qmk/qmk_firmware/tree/master/keyboards/ploopyco/trackball#drag-scroll&quot;&gt;firmware option&lt;/a&gt;
to use the trackball for scrolling.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_scrollwheel.Bs00-Lx2_Z1lRLMt.webp&quot; alt=&quot;scroll wheel&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;568&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;All the buttons are on now.&lt;/p&gt;
&lt;img src=&quot;/_astro/05_main_buttons.B8diWksH_C6zXQ.webp&quot; alt=&quot;main buttons&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;These are the bearings for the trackball.
There’s a little jig to push the rods through.&lt;/p&gt;
&lt;img src=&quot;/_astro/06_bearings.DTvnvnrn_ZNsXLf.webp&quot; alt=&quot;bearings&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;585&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;It took a bit of effort to put the top on as it’s finicky to get around the mouse buttons.&lt;/p&gt;
&lt;img src=&quot;/_astro/07_top_on.oz6Ij4XA_1twtUa.webp&quot; alt=&quot;top on&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;All done!&lt;/p&gt;
&lt;img src=&quot;/_astro/08_complete.DjUZhURc_1DVrjt.webp&quot; alt=&quot;complete&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I also setup my own &lt;a href=&quot;https://github.com/enochchau/qmk_firmware/blob/d872a5d7681fde8ce52713bc3a8c71779f47d28f/keyboards/ploopyco/trackball/keymaps/ec965/keymap.c&quot;&gt;mouse keymap&lt;/a&gt;
&lt;a href=&quot;https://github.com/enochchau/qmk_firmware/blob/d872a5d7681fde8ce52713bc3a8c71779f47d28f/keyboards/ploopyco/trackball/keymaps/ec965/config.h&quot;&gt;config&lt;/a&gt; through QMK.
The default options make configuration really easy.&lt;/p&gt;
&lt;p&gt;One problem I did run into was that I couldn’t get &lt;code&gt;LT&lt;/code&gt; to work with &lt;code&gt;KC_BTN5&lt;/code&gt;.
The
&lt;a href=&quot;https://github.com/enochchau/qmk_firmware/blob/d872a5d7681fde8ce52713bc3a8c71779f47d28f/keyboards/ploopyco/trackball/keymaps/drag_scroll/keymap.c&quot;&gt;demo keymap&lt;/a&gt;
for drag scroll shows the 5th mouse button used as &lt;code&gt;LT(1, KC_BTN5)&lt;/code&gt; but in practice this doesn’t work.
The QMK docs mention a &lt;a href=&quot;https://docs.qmk.fm/#/feature_layers?id=caveats&quot;&gt;caveat&lt;/a&gt;
around &lt;code&gt;LT&lt;/code&gt; where you can only use keycodes in the &lt;a href=&quot;https://docs.qmk.fm/#/keycodes_basic&quot;&gt;basic keycode set&lt;/a&gt;,
which &lt;code&gt;KC_BTN5&lt;/code&gt; is not a part of.
This was a bit of a bummer since I really would have liked all 4 mouse buttons to always be tap functional
rather than having a dedicated layer toggle key.
It’s okay though, can’t have your cake and eat it too.&lt;/p&gt;
&lt;p&gt;I ended up using the right button as a layer toggle.
This layer includes the drag scroll toggle, DPI toggle, and mouse button 5.&lt;/p&gt;
&lt;p&gt;Getting used to the trackball has been entertaining.
I had to scale down the DPI a whole lot from the default 1200DPI to 400DPI.
My hand precision and dexterity are shaky at best and it only gets worse after coffee.
Being a surgeon was ruled out real early in my life.&lt;/p&gt;
&lt;p&gt;I’ll see if I stick with using this mouse as a daily driver.
The location of the mouse buttons is very different from a traditional mouse so it will definitely take some time to get used to.
I also keep accidentally touching the mouse wheel.&lt;/p&gt;
&lt;p&gt;I wonder if I’ll be any good at FPS or League with this thing.&lt;/p&gt;</description><pubDate>Sat, 20 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Nope</title><link>https://enochchau.com/blog/2022/nope/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/nope/</guid><description>&lt;p&gt;It’s August 19th and I’m walking.
On my previous walks, I always pass by a beautiful Blue Bottle on Jackson Street.
This morning, I decided I’d finally take a look inside.&lt;/p&gt;
&lt;p&gt;Starting at the Embarcadero Bart Station, I’m surrounded by tall sky scrapers on all sides.
The concrete buildings would have cast shadows into the street if it weren’t for the overcast clouds.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_sf_towers.DvQhf--8_ZdvK09.webp&quot; alt=&quot;Sf Towers&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The side streets off market around this part of town are quite nice to walk on.
There’s a fair number of cross walks in unexpected places.&lt;/p&gt;
&lt;img src=&quot;/_astro/01_north_beach_walking.Cl4HNiwH_1wIV0v.webp&quot; alt=&quot;North Waterfront walking&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Today I finally took a stop at Walton Square.
There’s a stair case that goes up to the apartment towers above.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_above_walton_square.D8HMB2oL_2fN082.webp&quot; alt=&quot;Walton square apartment block bridge&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;It’s funny because I wanted to go up to get a picture of Jackson Street but there was already another dude who had the same idea.
With his phone camera out, he looked like a tourist, taking a full panoramic video of the surrounding.
I can only imagine what he did with his photos.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_down_jackson.URIA_En0_Z19NEkL.webp&quot; alt=&quot;Down Jackson Street&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I also passed by the parking garage I used to park at.
They have a good deal, it’s $16 to park if you get in before 9AM.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_my_parking_garage.DBmSY2iK_Zh8B7J.webp&quot; alt=&quot;My parking garage&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;My mom was in Taipei for the last year so I more or less was driving her car like it was mine.
Now that she’s home and working, I’d feel a little guilty if I took my Dad’s car to work and stranded him at home.
So I’ve been taking Bart lately, which gives me a chance to go on these morning walks.
Another plus is that it’s good for the environment to take public transport.&lt;/p&gt;
&lt;p&gt;Today’s Blue Bottle is around where my friend Will lives.
It’s close to China Town with rows of brick buildings.&lt;/p&gt;
&lt;img src=&quot;/_astro/05_towards_blue_bottle.CpYSxaHW_2fG56Y.webp&quot; alt=&quot;Towards blue bottle&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;This Blue Bottle’s interior has a modern minimalist vibe to it.
I recognize that minimalism doesn’t leave too much room for expression but I enjoy it nonetheless.&lt;/p&gt;
&lt;img src=&quot;/_astro/06_blue_bottle.DpQrZdxA_Zw8L46.webp&quot; alt=&quot;Blue bottle inside&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I got a single origin pour over coffee, made fresh by the barista Alejandro.
He recognized my name as being biblical since he grew up in a Church.
But like me, he doesn’t go the church anymore.
He said his friend has been getting him into Free Masonry.
It seems to be a secretive society as he couldn’t really tell me what they do.
The pour over was delicious though.
A fresh made pour over is always a treat since most places will serve you pre-brewed pot coffee.&lt;/p&gt;
&lt;p&gt;I took my morning meeting at Blue Bottle before walking back towards the office.&lt;/p&gt;
&lt;img src=&quot;/_astro/07_sculptures.xd2UOOBh_1aWLO1.webp&quot; alt=&quot;sculptures&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;There’s a wooden sculpture installation at the office complex adjacent to Walton Square.
That reminds me, I haven’t been to an art gallery in awhile.
I used to be more involved with my artistic side in high school and college but I’ve lost touch in recent years.
Maybe that’s why I’m writing and photographing now…&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;after-work&quot;&gt;After Work&lt;/h2&gt;
&lt;p&gt;After work, I was going to meet up with some friends to see Jordan Peele’s ‘Nope’.
I’m not a fan of horror films but I figured it’d be fun to go out on a Friday.&lt;/p&gt;
&lt;p&gt;Going from Pier 5 to Yerba Beuna, I passed by the Sales Force building.&lt;/p&gt;
&lt;img src=&quot;/_astro/08_sales_force_building.C_McCUUw_25dxNf.webp&quot; alt=&quot;sales force building&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;That building went up sometime when I was in college so it doesn’t really register with my days exploring the city in high school.
Some people thinks it’s an eyesore on the city skyline.
They lovingly call it ‘Zeus’s Dildo’.
I don’t care much for it. It’s just another building.&lt;/p&gt;
&lt;p&gt;We planned to meet at AMC Metreon in Yerba Beuna park.
You can just see it in the back of this photo.&lt;/p&gt;
&lt;img src=&quot;/_astro/09_towards_metreon.CvDnVQv5_ZKY4i3.webp&quot; alt=&quot;Towards Metreon&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I’d been to Yerba Beuna Park a few times in high school: ice skating at the rink or to see a dance show at the theater.&lt;/p&gt;
&lt;img src=&quot;/_astro/10_yerba_buena_theatre.nYrExv5i_Z1brtiu.webp&quot; alt=&quot;Yerba beuna theatre&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;When I was in high school, I had two close friends named Alex and Avery who were at community college.
They found an art club that got free tickets to fine art shows.
So they would take me out to see things like dance shows, art galleries, and on one occasion, the opera.
I’m thankful for them taking me under their wings during my senior year of high school.
I feel like those years were formative for my love of art.&lt;/p&gt;
&lt;img src=&quot;/_astro/11_sf_moma.DdUbaRMP_eUIIp.webp&quot; alt=&quot;SF Moma&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Speaking of art, Yerba Beuna Park is right across the street form the SF MOMA.
The MOMA was free for minors so it wasn’t uncommon for my high school friends and I to go there on a weekend for fun.
High schooler’s don’t have much money so anything to do that’s free is something to do.
I haven’t been in awhile now.
I’ve got to make a trip out one of these days.&lt;/p&gt;
&lt;p&gt;‘Nope’ was good.
It was a real thriller, no grotesque horror, it was all jump scares and suspense.
I liked it.&lt;/p&gt;
&lt;p&gt;After the movie, we decided to go to check out bars nearby for drinks.
I like going out to drink, but mostly just for the conversations.
Words flow more freely with a little bit of alcoholic social lubrication.
What can I say, I’m a conversation enthusiast.&lt;/p&gt;
&lt;p&gt;We stopped by Novelle but it was a bit too crowded so I suggested House of Shields.&lt;/p&gt;
&lt;img src=&quot;/_astro/12_novelle.DRjdWvax_ZVA6kH.webp&quot; alt=&quot;Novelle&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;It was only a shy 400ft away.&lt;/p&gt;
&lt;img src=&quot;/_astro/13_sales_force_tower_night.BMAXglWL_SolRC.webp&quot; alt=&quot;Sales force tower at night&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/14_house_of_shields.BogVkEYY_1h534y.webp&quot; alt=&quot;House of Shields&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I’d first heard of House of Shields through &lt;a href=&quot;https://www.reddit.com/r/sanfrancisco/&quot;&gt;r/sanfrancisco&lt;/a&gt;.
There’s a user named &lt;a href=&quot;https://www.reddit.com/user/jimmyjah/&quot;&gt;u/jimmyjah&lt;/a&gt; who posts occasionally to setup meet ups at House of Shields.
The meet ups are always off the cuff, day of, so I haven’t been around for one yet.
I asked the bartender James about the meet ups and he said there’s always interesting people.
Apparently, one time, the CEO of reddit came by.&lt;/p&gt;
&lt;img src=&quot;/_astro/15_house_of_shields_inside.CaSBQ9jC_ZV75yT.webp&quot; alt=&quot;Inside House of Shields&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I have no idea who the guy in the painting is but this is the inside of House of Shields.
The decor reminds me of a 50s style bar.
All the bartenders wear shirts, vests, and ties.
It’s got a fancy, yet dive bar-y vibe going on.&lt;/p&gt;
&lt;img src=&quot;/_astro/16_drinks.DHsLKpBQ_1OL3E4.webp&quot; alt=&quot;Drinks&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Our group was six and I ended up being the designated drink retrieval representative.
I can still remember the orders.
The first round was a Proseco, a Pino Nior, a Russian River IPA, a Penicillin, a Gold Rush, and a Pisco Punch.
The second round was a Diet Coke, a Cabernet Sauvignon, 2 Russian Rivers, a Hen House beer, and a Sazerac.&lt;/p&gt;
&lt;p&gt;I got a Sazerac the last time I was at House of Shields as well.
I like the boozy sweetness of drinks like an Old Fashioned or a Sazerac.&lt;/p&gt;
&lt;p&gt;I’ve been drinking more whiskey lately.
My Mom came through and got me a bottle of &lt;a href=&quot;https://www.kavalanwhisky.com/en/product.php?act=view&amp;id=13&quot;&gt;Kavalan Single Cask Whiskey&lt;/a&gt;
at duty free before coming back from Taiwan.
Those bottles go for around &lt;a href=&quot;https://www.totalwine.com/spirits/scotch/single-malt/kavalan-whisky-solist/p/151857750&quot;&gt;$250&lt;/a&gt;
for 750ml in the US but Mom got a 1L bottle for just $160.&lt;/p&gt;
&lt;p&gt;All in all, it was a fun night.
And I was just in time to catch the penultimate train home.&lt;/p&gt;</description><pubDate>Fri, 19 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Tenderloin to Embarcadero</title><link>https://enochchau.com/blog/2022/tenderloin-to-embarcadero/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/tenderloin-to-embarcadero/</guid><description>&lt;p&gt;Lately, I’ve been taking walks in the morning before going to the office.
I like to get coffee or pastries for breakfast while exploring different parts of the city.
There’s something different about experiencing a city from the ground compared to a car or bus.&lt;/p&gt;
&lt;p&gt;Today I walked from the Tenderloin to Embarcadero.
&lt;a href=&quot;https://www.joannwphotos.com/&quot;&gt;Joann&lt;/a&gt; told me about a French bakery called &lt;a href=&quot;https://arsicault-bakery.com/home&quot;&gt;Arsicault&lt;/a&gt;.
They have a location near Civic Center in the Tenderloin so I decided, last night, to make a stop before going to work.&lt;/p&gt;
&lt;img src=&quot;/_astro/00_arisicult_croissant.DVtf8j-D_TnEnQ.webp&quot; alt=&quot;Arsicault Croissant&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The almond croissant was deliciously buttery, crispy and flaky.
The cappuccino was perfectly foamy.
I generally rate cappuccinos by how ‘creamy’ the foam is.&lt;/p&gt;
&lt;p&gt;Afterwards, I went towards a litte banh mi shop a few blocks away called &lt;a href=&quot;https://www.yelp.com/biz/saigon-sandwich-san-francisco&quot;&gt;Saigon Sandwhich&lt;/a&gt;.&lt;/p&gt;
&lt;img src=&quot;/_astro/01_tenderloin.D0GWNYtV_Z27piO5.webp&quot; alt=&quot;Tenderloin walking&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;All over the Tenderloin, there are people wearing black long sleeves with the &lt;a href=&quot;https://urban-alchemy.us/&quot;&gt;Urban Alchemy&lt;/a&gt; logo on them.
I had no idea who these people were but looking them up now, it seems like they try to rehabilitate neighborhoods with bad reputations.
I suppose I felt safer.
There was a dude smoking crack on the street and one of the Urban Alchemy guys was like:&lt;/p&gt;
&lt;p&gt;“Hey, hey, hey, you can’t be doing that here!”&lt;/p&gt;
&lt;p&gt;I didn’t stick around for the end of that interaction, but it’s nice to know someone’s trying to do something about the Tenderloin.
I’ve always found it a little disappointing that the Vietnamese part of SF is in the worst part of the city so it’s nice that that might change slowly.&lt;/p&gt;
&lt;p&gt;And then I was at Saigon Sandwich.&lt;/p&gt;
&lt;p&gt;I don’t think they’ve changed their prices in about 10 years.
The sandwiches are still the same price of $5 since the first time I came here some 4 years ago with my friend Rebecca.
We met when I was studying abroad in Taiwan during the summer of 2017 and have met up here and there in the past 5 years.
She doesn’t live in the Bay Area but I try to make a point to meet up anytime I know she’s in SF.&lt;/p&gt;
&lt;p&gt;Then off I was to the office.&lt;/p&gt;
&lt;img src=&quot;/_astro/02_church_looking.CBFArr7T_Z1YWHkc.webp&quot; alt=&quot;Church looking building&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I work at an office at Pier 5 off the Embarcadero.
Well, really, I work remote, but we use our investors office to meet up from time to time.
Saigon Sandwhich is about a 2 mile walk from the office.
But I wanted to stop by Huntington Park so the total distance would be a little longer.&lt;/p&gt;
&lt;p&gt;There were some interesting looking buildings on my walk up.
Part way up the hill was a Victorian style house juxtaposed against the backdrop of a massive white concrete building.&lt;/p&gt;
&lt;img src=&quot;/_astro/03_old_building_towards_park.Ci5bs4TQ_mc60g.webp&quot; alt=&quot;Old building towards the park&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The Victorian style architecture in the city is pleasant to the eyes.
It gives the city an air of elegance to an older time.
Granted, having been inside my share of these buildings, they’re are all aging now.&lt;/p&gt;
&lt;p&gt;At the top of the hill was Huntington Park.
I’ve heard it’s a nice park, and well, now I know it’s a nice park.&lt;/p&gt;
&lt;img src=&quot;/_astro/04_huntington_park.CcftAwya_17Gf5l.webp&quot; alt=&quot;Huntington Park&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;It’s a little bit of greenery surrounded by some older mansion or church type buildings.
I would think that these surrounding buildings would be connected to the park in some way but honestly I’m not sure if they have any relation.&lt;/p&gt;
&lt;p&gt;San Francisco’s hills are hard for walking, but the elevation does have it’s perks.
Today, I could see all the way, through the buildings, to the Bay Bridge.&lt;/p&gt;
&lt;img src=&quot;/_astro/05_leaving_park.NaMR7D7s_Z1OnysG.webp&quot; alt=&quot;Leaving the park&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;The city can be blanketed in fog on any given day, even during the summer, but today was a clear sunny day.
Just my luck.&lt;/p&gt;
&lt;p&gt;On my past walks, I’ve frequented Little Italy and China Town.
Today’s path took me through a familiar part of town.
Near China Town is one of my favorite coffee shops in the city, &lt;a href=&quot;https://www.thecoffeemovement.com/&quot;&gt;The Coffee Movement&lt;/a&gt;.
There used to be an 70s style VW bus parked out front but it got replaced by parklet a few weeks ago.
Today, there was a little crowd outside drinking coffee and chatting.
I was already running late so I decided not to stop for another cup of coffee.&lt;/p&gt;
&lt;p&gt;Going through China town is always interesting.&lt;/p&gt;
&lt;img src=&quot;/_astro/06_china_town.CzcGV5Ne_ZDvcpq.webp&quot; alt=&quot;china town&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;In the morning, there’s always a bustle of old Cantonese people out shopping.
Racks of vegetables laid out at street level remind me of the wet markets in Taipei.
Everything about it has a strangely homey feeling.
Maybe because I could see any of these people being my grandmother.&lt;/p&gt;
&lt;p&gt;Farther down, there’s a coffee shop in a triangle building.&lt;/p&gt;
&lt;img src=&quot;/_astro/07_triange_coffee_shop.bQ9UH-9y_Z2koRnv.webp&quot; alt=&quot;Triangle coffee shop&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;These triangle buildings are such a lovely shape compared the monotony of square buildings.&lt;/p&gt;
&lt;img src=&quot;/_astro/08_path_near_embarcadero.tLPkDRDq_1Pqq8S.webp&quot; alt=&quot;Path near Embarcadero&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I love finding little nuggets of greenery in the concrete jungle.&lt;/p&gt;
&lt;img src=&quot;/_astro/09_embarcadero.BycOZvOP_ZeTwxx.webp&quot; alt=&quot;Embarcadero&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;And finally, across the street from the water front.
I was just a minute late to my first meeting so I’ll count my brisk pace as a success.&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;after-work&quot;&gt;After Work&lt;/h2&gt;
&lt;p&gt;After work, my coworkers and I went out for drinks at the ferry building.
I don’t have much to say but I do have some photos to share.&lt;/p&gt;
&lt;img src=&quot;/_astro/10_ferry_building.Baa-qeKf_Z12rPna.webp&quot; alt=&quot;ferry building&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;a href=&quot;https://humphryslocombe.com/&quot;&gt;Humphry Slocombe&lt;/a&gt; always has a line out front, even at 7PM.
I’ve had their ice cream from the grocery store but I still haven’t had the chance to stop by this location.&lt;/p&gt;
&lt;img src=&quot;/_astro/11_ferry_building.Cam-vwi3_2rpVWe.webp&quot; alt=&quot;ferry building&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Of course, the beautiful Bay and Bay Bridge in the fading light.&lt;/p&gt;
&lt;img src=&quot;/_astro/13_bay_bridge.3Ahub4O9_g1s8F.webp&quot; alt=&quot;bay bridge&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Our interns are ending their internship next week so this was a nice day to drink and talk after work.&lt;/p&gt;
&lt;img src=&quot;/_astro/12_fort_point_brews.Bq5WLVhZ_1lQJpf.webp&quot; alt=&quot;Fort point brews&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/14_the_bird.N0HBxH08_1uWvJm.webp&quot; alt=&quot;the bird with the interns&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;This was my first summer being an intern mentor and I’d like to think I’ve done as well as I could.
I was worried at first that I wouldn’t do a good job but I think they’ve both grown quite a bit since they started.
I wish them the best.
Maybe they’ll be back next year when they graduate but working at a startup always has it’s share of uncertainty.&lt;/p&gt;</description><pubDate>Thu, 18 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Choosing a Terminal Emulator</title><link>https://enochchau.com/blog/2022/choosing-a-terminal/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/choosing-a-terminal/</guid><description>&lt;p&gt;I’ll admit it. I’ve been terminal hopping again.&lt;/p&gt;
&lt;p&gt;I use 3 computers: a MacBook for work, a Windows desktop PC, and a Linux personal laptop running &lt;a href=&quot;https://pop.system76.com/&quot;&gt;Pop!_OS&lt;/a&gt;.
For the past year or so, I’ve been using &lt;a href=&quot;https://github.com/alacritty/alacritty&quot;&gt;Alacritty&lt;/a&gt; on Linux and MacOS and it works great.
But my Linux laptop is aging and I’ve begun to do more and more development on WSL with my Windows desktop.
I’ve been using &lt;a href=&quot;https://github.com/microsoft/terminal&quot;&gt;Windows Terminal&lt;/a&gt; but it’s slow and there are much better options out there.&lt;/p&gt;
&lt;p&gt;On my search for a terminal emulator, I’ve set 3 requirements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt;: this one speaks for itself&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross platform&lt;/strong&gt;: I don’t want to have to spend time re-configuring different terminals for different operating systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Doesn’t need tabs or splits&lt;/strong&gt;: I use tmux for that.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And I have a few bonuses:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ligatures&lt;/strong&gt;: not a necessity but it’s nice to have&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Undercurl&lt;/strong&gt;: I use &lt;a href=&quot;https://neovim.io/&quot;&gt;Neovim&lt;/a&gt; as my editor and undercurl looks real nice.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;alacritty&quot;&gt;&lt;a href=&quot;https://github.com/alacritty/alacritty&quot;&gt;Alacritty&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve been using Alacritty on Linux and MacOS for the past year and it’s great.
Alacritty claims to be the fastest terminal emulator and in my experience I’ve found that to be true.
That being said, they sacrifice certain niceties such as tabs, panes, and ligatures for performance.
Undercurl is coming sometime soon but isn’t in the stable release yet.&lt;/p&gt;
&lt;p&gt;All that is fine for my usage but the one thing that bugs me is mouse support for WSL.
There’s been &lt;a href=&quot;https://github.com/alacritty/alacritty/issues/1663&quot;&gt;issues&lt;/a&gt; with mouse support in WSL for some time now
with no clear path to it getting fixed.&lt;/p&gt;
&lt;h2 id=&quot;kitty&quot;&gt;&lt;a href=&quot;https://sw.kovidgoyal.net/kitty/&quot;&gt;Kitty&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Kitty is cool. It was the first terminal to support undercurl.
Kitty also includes ligatures, tabs, and splits.
It’s GPU accelerated and it feels fast.
Unfortunately, it only supports MacOS and Linux and I’m not willing to keep a separate terminal config just for WSL.&lt;/p&gt;
&lt;h2 id=&quot;hyper&quot;&gt;&lt;a href=&quot;https://hyper.is/&quot;&gt;Hyper&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve tried Hyper once. That’s about it.
It’s written in TypeScript with Electron which is an immediate ‘no’ for me.
Electron apps are notorious for using lots of memory.
I enjoy writing TypeScript as a language but I know first hand: JavaScript is not fast.&lt;/p&gt;
&lt;h2 id=&quot;wezterm&quot;&gt;&lt;a href=&quot;https://wezfurlong.org/wezterm/&quot;&gt;WezTerm&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WezTerm is a terminal emulator written in Rust and it’s cross platform.
It supports ligatures, undercurl, splits, and tabs.
From my usage, it doesn’t feel as fast as Kitty or Alacritty but its still pretty fast.
The best thing is that it works on all my target Operating Systems.
There’s even &lt;a href=&quot;https://wezfurlong.org/wezterm/config/lua/WslDomain.html?highlight=wsl#wsldomain&quot;&gt;built in support&lt;/a&gt; for WSL.&lt;/p&gt;
&lt;p&gt;That pretty much checks all my boxes.&lt;/p&gt;
&lt;h2 id=&quot;other&quot;&gt;Other&lt;/h2&gt;
&lt;p&gt;I also looked into using &lt;a href=&quot;https://st.suckless.org/&quot;&gt;st&lt;/a&gt; and &lt;a href=&quot;https://wiki.archlinux.org/title/rxvt-unicode&quot;&gt;urxvt&lt;/a&gt;.
Both are classic minimalist terminals for Linux.
I’ve mostly seen them being used with Arch and tiling window managers.
I would have given them a shot but unfortunately, my cross platform needs restricts me somewhat in this regard.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Well, I’m picking WezTerm. On WSL, it feels faster than Windows Terminal which is already a huge plus.
The configs are written in Lua which is always a fun language to write.
I could even use &lt;a href=&quot;https://fennel-lang.org/&quot;&gt;Lisp&lt;/a&gt; to configure it if I wanted.&lt;/p&gt;
&lt;p&gt;Maybe I’ll consider hopping again once Alacritty fixes mouse events in WSL and gets undercurl in stable.
Who knows, maybe the speed difference between WezTerm and Alacirtty will convince me to have separate setups
for WSL and MacOS/Linux.&lt;/p&gt;
&lt;p&gt;For now, I’m content. Until next times terminal hopping.&lt;/p&gt;</description><pubDate>Tue, 16 Aug 2022 00:00:00 GMT</pubDate></item><item><title>College Recipes</title><link>https://enochchau.com/blog/2022/college-recipes/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/college-recipes/</guid><description>&lt;p&gt;A collection of recipes from my college days.
It’s somewhat nostalgic to look back on these.
Most of these recipes were written during the height of
&lt;a href=&quot;https://www.youtube.com/channel/UCbpMy0Fg74eXXkvxJrtEn3w&quot;&gt;Bon Appetit&lt;/a&gt; and
&lt;a href=&quot;https://www.youtube.com/c/bingingwithbabish&quot;&gt;Binging with Babish’s&lt;/a&gt; popularity on food YouTube.
I would read several recipes online and watch some videos before compiling my notes into my own recipe.
They are written on note cards, grocery receipts, and post it notes.
I still have all the physical copies but they’re starting to fade and fall apart with time.&lt;/p&gt;
&lt;div id=&quot;recipe-container&quot; data-astro-cid-bkt72kle&gt; &lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/ba-s_best_banana_bread.DadhoVP5_Z1wv2jR.webp&quot; alt=&quot;ba-s_best_banana_bread&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;1015&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/banana_bread_back.Bi89sNTA_SL185.webp&quot; alt=&quot;banana_bread_back&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;925&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/banana_bread_front.Bcrioml8_27FgPX.webp&quot; alt=&quot;banana_bread_front&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;988&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/bechamel.CXdiyCvn_1f80bI.webp&quot; alt=&quot;bechamel&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;558&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/blueberry_muffins.CBW2tJ6n_Z2uxSoT.webp&quot; alt=&quot;blueberry_muffins&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;622&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/chana_masala.DFAlty55_Z22p8SQ.webp&quot; alt=&quot;chana_masala&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;1154&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/cheese_cake.zi3CguHr_10XM6F.webp&quot; alt=&quot;cheese_cake&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;622&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/cheese_sauce.CxQwQLh9_Z2f4AtQ.webp&quot; alt=&quot;cheese_sauce&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;959&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/chicken_marsala_back.1rIxEQPp_Z17cywf.webp&quot; alt=&quot;chicken_marsala_back&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;926&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/chicken_marsala_front.CjGOMdP4_ZHvTbM.webp&quot; alt=&quot;chicken_marsala_front&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;965&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/chocolate_banana_cake_back.BQheOVn6_1Tg8E9.webp&quot; alt=&quot;chocolate_banana_cake_back&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;772&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/chocolate_banana_cake_front.DxemZ38P_1VrgBU.webp&quot; alt=&quot;chocolate_banana_cake_front&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;696&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/chocolate_chip_cookies_back.BRvMo032_1vxQr.webp&quot; alt=&quot;chocolate_chip_cookies_back&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;925&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/chocolate_chip_cookies_front.D2ZAtLgp_Z2iv8C.webp&quot; alt=&quot;chocolate_chip_cookies_front&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;922&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/chocolate_chip_cookies_original.C0yAKYlN_Z1OwOVi.webp&quot; alt=&quot;chocolate_chip_cookies_original&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;568&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/double_tree_cookies_back.J5dX1_-T_FVcvv.webp&quot; alt=&quot;double_tree_cookies_back&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;951&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/double_tree_cookies_front.bJQfhZOe_111tB8.webp&quot; alt=&quot;double_tree_cookies_front&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;957&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/egg_tart.COqfC_Fd_1We8DY.webp&quot; alt=&quot;egg_tart&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;572&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/gyudon.C2fnXOxG_21X9hv.webp&quot; alt=&quot;gyudon&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;690&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/kombucha_feed.DhFsCm43_Z24SSRt.webp&quot; alt=&quot;kombucha_feed&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;794&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/lemon_bars.CbkYbc2l_Zkc4xV.webp&quot; alt=&quot;lemon_bars&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;597&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/meatballs.CRtXYll6_ZdrExA.webp&quot; alt=&quot;meatballs&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;1173&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/miso_soup.BRZa9alN_udTgs.webp&quot; alt=&quot;miso_soup&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;582&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/oat_cookies.DynzHZta_vJoqf.webp&quot; alt=&quot;oat_cookies&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;567&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/okyakodon.CpM_OHP7_11HxrX.webp&quot; alt=&quot;okyakodon&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;945&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/oyakodon.ANC9bQ_M_1cjYUz.webp&quot; alt=&quot;oyakodon&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;578&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/pancakes.EkWxwW4x_Z1OmVVK.webp&quot; alt=&quot;pancakes&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;1210&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/pancakes_for_one.C_r5vQ-g_Z8yss9.webp&quot; alt=&quot;pancakes_for_one&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;1061&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/ragout.L-9RcabV_187oef.webp&quot; alt=&quot;ragout&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;1043&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/tiki_masala.CcD3CeMm_Z243Ru8.webp&quot; alt=&quot;tiki_masala&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;1101&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt;&lt;div class=&quot;img-container&quot; data-astro-cid-bkt72kle&gt; &lt;img src=&quot;/_astro/xue_q_bing.DyM9xSJg_Z3ou4x.webp&quot; alt=&quot;xue_q_bing&quot; data-astro-cid-bkt72kle=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;600&quot; height=&quot;1019&quot; class=&quot;full-image&quot;&gt; &lt;/div&gt; &lt;/div&gt; &lt;dialog id=&quot;recipe-viewer&quot; data-astro-cid-bkt72kle&gt;&lt;/dialog&gt; &lt;script type=&quot;module&quot; src=&quot;/home/runner/work/website/website/src/components/Recipes.astro?astro&amp;type=script&amp;index=0&amp;lang.ts&quot;&gt;&lt;/script&gt; </description><pubDate>Thu, 11 Aug 2022 00:00:00 GMT</pubDate></item><item><title>Eslint Cache in CI</title><link>https://enochchau.com/blog/2022/eslint-cache-ci/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/eslint-cache-ci/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/eslint-gh-actions-cache&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At &lt;a href=&quot;http://gatsby.events&quot;&gt;Gatsby&lt;/a&gt;, we have a monorepo that contains all our internal packages.
Our CI pipeline will lint, type check, test, and build our entire system.
As we add more packages, CI times have taken longer and longer.
I’m always on the lookout to shave off a few seconds or minutes of our CI time.
One of our issues is that linting the entire monorepo can take about 2 - 2.5 minutes.&lt;/p&gt;
&lt;p&gt;I read &lt;a href=&quot;https://david.deno.dev/posts/faster-prettier-with-dprint/&quot;&gt;a post&lt;/a&gt; on hacker news
about speeding up prettier in CI by using a cache with d-print.
Knowing that eslint has a caching option, I wondered if I could do the same thing with our CI lint step.&lt;/p&gt;
&lt;p&gt;Eslint has &lt;a href=&quot;https://eslint.org/docs/user-guide/command-line-interface#caching&quot;&gt;several flags&lt;/a&gt; related to caching:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--cache&lt;/code&gt;: enables caching&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--cache-strategy&lt;/code&gt;: choose a cache strategy from either &lt;code&gt;metadata&lt;/code&gt; (last edit time) or &lt;code&gt;content&lt;/code&gt; (checksum)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--cache-location&lt;/code&gt;: location to write the cache file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our full command might look something like the following. Which we can then save in our &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFFFFF&quot;&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  &amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    &amp;quot;lint&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;eslint ./src/ --cache --cache-strategy content --cache-location ~/.cache/eslint/demo&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we have to use the &lt;code&gt;content&lt;/code&gt; cache strategy.
Git doesn’t save last edit times (&lt;code&gt;metadata&lt;/code&gt;).
If we were to use &lt;code&gt;metadata&lt;/code&gt; strategy, when we checkout our repo in CI, we won’t get a valid cache hit.&lt;/p&gt;
&lt;p&gt;Then we can configure the cache action in github actions:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;CI&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  Explore-GitHub-Actions&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    runs-on&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    steps&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;Check out repository code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;actions/checkout@v3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;Use Node.js 16.x&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;tests-workflow&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;actions/setup-node@v2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;          node-version&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;16.14.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;          cache&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;npm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;Cache&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;actions/cache@v3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;          path&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;            ~/.cache/eslint/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;          key&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;${{ runner.os }}-node-${{ hashFiles(&amp;#39;**/package-lock.json&amp;#39;, &amp;#39;**/.eslintrc.js&amp;#39;) }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;Install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        run&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;npm ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;Lint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        run&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;npm run lint&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We must configure out actions cache path to be the same as our Eslint cache location.
For our cache key, I use the checksum of &lt;code&gt;pacakge-lock.json&lt;/code&gt; and &lt;code&gt;.eslintrc.js&lt;/code&gt;.
That way, the cache will bust if we make any changes to our Eslint config or update our dependencies.&lt;/p&gt;
&lt;p&gt;The first run will still take the same amount of time as before since we have not yet built our cache.
This run took about 11s.&lt;/p&gt;
&lt;img src=&quot;/_astro/without-cache.BwGkWk6e_28TRpA.webp&quot; alt=&quot;initial eslint CI run&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;509&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Subsequent runs will be much faster. This run took about 1s.&lt;/p&gt;
&lt;img src=&quot;/_astro/with-cache.DcrGeeYU_Z1IQUph.webp&quot; alt=&quot;subsequent eslint CI run&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;509&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;You can find all the code used in this demo in &lt;a href=&quot;https://github.com/enochchau/eslint-gh-actions-cache&quot;&gt;this repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After implementing an eslint cache for our monorepo at Gatsby, our lint times went from about 2 minutes to 6 seconds with a cache hit.&lt;/p&gt;</description><pubDate>Mon, 25 Apr 2022 00:00:00 GMT</pubDate></item><item><title>Date Issues</title><link>https://enochchau.com/blog/2022/date-issues/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/date-issues/</guid><description>&lt;p&gt;I found an interesting difference between Firefox and Chrome recently in this
very website.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Date&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;April 2022&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The snippet above will parse to an accurate date in Chrome and Node.js but it
will result in an invalid date in Firefox. This little difference broke the
table of contents for this site’s blog.&lt;/p&gt;</description><pubDate>Wed, 06 Apr 2022 00:00:00 GMT</pubDate></item><item><title>Vite Dev Server Transforms</title><link>https://enochchau.com/blog/2022/vite-dev-server-transforms/</link><guid isPermaLink="true">https://enochchau.com/blog/2022/vite-dev-server-transforms/</guid><description>&lt;p&gt;While creating &lt;a href=&quot;https://github.com/enochchau/vite-plugin-rss&quot;&gt;&lt;code&gt;vite-plugin-rss&lt;/code&gt;&lt;/a&gt;, I
ran into a peculiar issue of serving an in memory XML file using Vite’s dev
server.&lt;/p&gt;
&lt;p&gt;For a little background, &lt;code&gt;vite-plugin-rss&lt;/code&gt; has two modes, &lt;code&gt;&amp;#39;define&amp;#39;&lt;/code&gt; and
&lt;code&gt;&amp;#39;meta&amp;#39;&lt;/code&gt;, of which only &lt;code&gt;&amp;#39;meta&amp;#39;&lt;/code&gt; is currently relevant. In &lt;code&gt;&amp;#39;meta&amp;#39;&lt;/code&gt; mode, the
plugin uses Rollup’s
&lt;a href=&quot;https://rollupjs.org/guide/en/#inter-plugin-communication&quot;&gt;inter-plugin communication&lt;/a&gt;
to pass RSS data from another plugin’s &lt;code&gt;load&lt;/code&gt; or &lt;code&gt;tranform&lt;/code&gt; step into
&lt;code&gt;vite-plugin-rss&lt;/code&gt;. Another plugin can add meta data to a module which
&lt;code&gt;vite-plugin-rss&lt;/code&gt; can read at the &lt;code&gt;buildEnd&lt;/code&gt; phase to generate RSS XML items.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;buildEnd&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;items&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;mode&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;meta&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; moduleIds&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Array&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getModuleIds&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;()); &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// &amp;lt;- get the module ids&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; moduleInfo&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; moduleIds&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getModuleInfo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)); &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// &amp;lt;- find the module info from the ids&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    items&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; moduleInfo&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt; // &amp;lt;- get our RSS items&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ModuleInfo&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !!&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;rssItem&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;rssItem&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // generate the RSS XML&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; renderedXML&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; createRssFeed&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;channel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;items&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fileName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // add it as an emitted asset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;emitFile&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    fileName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fileName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fileName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    source&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;renderedXML&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;asset&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our issue arises when we want to generate our RSS XML file during development. I
did a little experimentation and found that the &lt;code&gt;buildEnd&lt;/code&gt; phase is not run when
the dev server is started. In fact, a lot of Rollup’s plugin phases are not run.
Only the &lt;code&gt;load&lt;/code&gt; and &lt;code&gt;tranform&lt;/code&gt; phases are run when using Vite’s dev server.&lt;/p&gt;
&lt;p&gt;For a little background, we should discuss how the Vite dev server works. Vite
utilizes native browser ES Module support to do minimal transforms before
serving files. Raw ESM files (or modules) can be sent to the browser without
having to generate an entire bundle during development. This provides faster hot
module reload times and an overall better developer experience.&lt;/p&gt;
&lt;p&gt;Now, let’s dig into the source code for Vite’s dev server. The Vite dev server
uses a
&lt;a href=&quot;https://github.com/vitejs/vite/blob/212d4548eeb366289c6c6fa6f86f94b261ed81f4/packages/vite/src/node/server/index.ts&quot;&gt;variety of middleware&lt;/a&gt;
to serve different kinds of files.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// Internal middlewares ------------------------------------------------------&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// request timer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;process&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;DEBUG&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;timeMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;root&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// cors (enabled by default)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;cors&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; serverConfig&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;cors&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;corsMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cors&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;boolean&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {} &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; cors&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// proxy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;proxy&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; serverConfig&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;proxy&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;proxyMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;httpServer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// base&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;base&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;baseMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// open in editor support&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/__open-in-editor&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;launchEditorMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// hmr reconnect ping&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;/__vite_ping&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; viteHMRPingMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;pong&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// serve static files under /public&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// this applies before the transform middleware so that these files are served&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// as-is without transforms.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;publicDir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;servePublicMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;publicDir&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// main transform middleware&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;transformMiddleware&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// etc...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, there is middleware for configuring CORS or serving static files
from the public directory.&lt;/p&gt;
&lt;p&gt;The middleware we will focus on is the
&lt;a href=&quot;https://github.com/vitejs/vite/blob/212d4548eeb366289c6c6fa6f86f94b261ed81f4/packages/vite/src/node/server/transformRequest.ts&quot;&gt;&lt;code&gt;transformMiddlware&lt;/code&gt;&lt;/a&gt;.
This is where the module transformation happens. When a transformation happens,
Vite needs to know what a module’s dependencies are and transform those modules
as well.&lt;/p&gt;
&lt;p&gt;Modules are tracked in a
&lt;a href=&quot;https://github.com/vitejs/vite/blob/212d4548eeb366289c6c6fa6f86f94b261ed81f4/packages/vite/src/node/server/moduleGraph.ts#L58-L228&quot;&gt;&lt;code&gt;ModuleGraph&lt;/code&gt;&lt;/a&gt;
which acts as a dependency graph and cache for a given project.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ModuleGraph&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  urlToModuleMap&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleNode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  idToModuleMap&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleNode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // a single file may corresponds to multiple modules with different queries&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  fileToModulesMap&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Set&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleNode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;&amp;gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  safeModulesPath&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Set&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ModuleGraph&lt;/code&gt; tracks each module as a
&lt;a href=&quot;https://github.com/vitejs/vite/blob/212d4548eeb366289c6c6fa6f86f94b261ed81f4/packages/vite/src/node/server/moduleGraph.ts#L14-L41&quot;&gt;&lt;code&gt;ModuleNode&lt;/code&gt;&lt;/a&gt;
which records meta data about the last transformation time, id, file type, and
more.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ModuleNode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;   * Public served url path, starts with /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;   * Resolved file system path + query&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  file&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  type&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;css&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  info&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleInfo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  meta&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Record&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  importers&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Set&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleNode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  importedModules&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Set&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleNode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  acceptedHmrDeps&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; Set&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;ModuleNode&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  isSelfAccepting&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  transformResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TransformResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  ssrTransformResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;TransformResult&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  ssrModule&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;Record&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;&amp;gt; | &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  lastHMRTimestamp&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;  lastInvalidationTimestamp&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; isDirectCSSRequest&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;css&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a file is requested by the client, the dev server will go to
&lt;a href=&quot;https://github.com/vitejs/vite/blob/212d4548eeb366289c6c6fa6f86f94b261ed81f4/packages/vite/src/node/server/transformRequest.ts#L70-L91&quot;&gt;look up the file&lt;/a&gt;
in the module graph. Then, Vite will transform the requested modules using a
&lt;a href=&quot;https://github.com/vitejs/vite/blob/212d4548eeb366289c6c6fa6f86f94b261ed81f4/packages/vite/src/node/server/pluginContainer.ts&quot;&gt;plugin container&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The plugin container is a way to run just the Rollup &lt;code&gt;load&lt;/code&gt; and &lt;code&gt;transform&lt;/code&gt;
phases on a given module. The code was adapted from the Rollup plugin container
created by
&lt;a href=&quot;https://github.com/preactjs/wmr/blob/main/packages/wmr/src/lib/rollup-plugin-container.js/&quot;&gt;&lt;code&gt;wmr&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Okay, so now we have a rough idea of what’s going on in the dev server. As for
my RSS XML file problem, I need to some how extract module info from the &lt;code&gt;load&lt;/code&gt;
and &lt;code&gt;transform&lt;/code&gt; steps for use in the &lt;code&gt;configureServer&lt;/code&gt; phase.&lt;/p&gt;
&lt;p&gt;Luckily, we can get all this information from
&lt;a href=&quot;https://github.com/vitejs/vite/blob/212d4548eeb366289c6c6fa6f86f94b261ed81f4/packages/vite/src/node/server/index.ts#L145-L297&quot;&gt;server configuration object&lt;/a&gt;.
The server provides
&lt;a href=&quot;https://github.com/vitejs/vite/blob/212d4548eeb366289c6c6fa6f86f94b261ed81f4/packages/vite/src/node/server/index.ts#L180-L185&quot;&gt;references&lt;/a&gt;
to both &lt;code&gt;server.pluginContainer&lt;/code&gt; and &lt;code&gt;server.moduleInfo&lt;/code&gt;. From these two
objects, we can get the same information that we were originally able to get in
the &lt;code&gt;buildEnd&lt;/code&gt; phase.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;configureServer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;  // serve feed.xml on dev server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;  server&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;middlewares&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;next&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      typeof&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; req&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;string&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      new&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; RegExp&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fileName&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;$`&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;items&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;mode&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &amp;quot;meta&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; devServerModuleIds&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Array&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;          server&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;moduleGraph&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;idToModuleMap&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;keys&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// &amp;lt;- get the module ids&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;        const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; moduleInfo&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; devServerModuleIds&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;          server&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;pluginContainer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getModuleInfo&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;// &amp;lt;- find the module info from the ids&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;        items&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; moduleInfo&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt; // &amp;lt;- get our RSS items&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; ModuleInfo&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; !!&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;rssItem&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;          .&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#E06C75;font-style:italic&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;rssItem&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; renderedXML&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt; createRssFeed&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;opts&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;channel&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;items&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; ??&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; [], &lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fileName&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; fileContent&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; Buffer&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;renderedXML&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;utf8&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; readStream&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#C678DD&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt; stream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;PassThrough&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      readStream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;fileContent&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;writeHead&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#98C379&quot;&gt;        &amp;quot;Content-Type&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&amp;quot;text/xml&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E5C07B&quot;&gt;      readStream&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;    next&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can serve the same RSS XML file using both the dev server and in
production builds. This is especially helpful for testing the RSS XML output as
I can point a local RSS Reader at the dev server’s URL to test the validity of
the generated XML.&lt;/p&gt;</description><pubDate>Sun, 03 Apr 2022 00:00:00 GMT</pubDate></item><item><title>Hacker News with SolidJS</title><link>https://enochchau.com/blog/2021/hacker-news/</link><guid isPermaLink="true">https://enochchau.com/blog/2021/hacker-news/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://enochchau.com/hn&quot;&gt;demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/hn&quot;&gt;source code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Implementing Hacker News using &lt;a href=&quot;https://github.com/solidjs/solid&quot;&gt;SolidJS&lt;/a&gt; and
the &lt;a href=&quot;https://github.com/HackerNews/API&quot;&gt;Hacker News API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;light mode&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/hn-light.CTdFHBxx_Z10W5FX.webp&quot; alt=&quot;Light Mode&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;464&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;dark mode&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/hn-dark.CXiNj2SV_Zidnm4.webp&quot; alt=&quot;Dark Mode&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;464&quot; class=&quot;image&quot;&gt; </description><pubDate>Mon, 01 Nov 2021 00:00:00 GMT</pubDate></item><item><title>GIS is Split</title><link>https://enochchau.com/blog/2021/gis/</link><guid isPermaLink="true">https://enochchau.com/blog/2021/gis/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/GIS&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GIS is a split keyboard that uses low profile choc switches. The key layout
draws inspiration from the
&lt;a href=&quot;https://github.com/josefadamcik/SofleKeyboard&quot;&gt;Sofle&lt;/a&gt; keyboard.&lt;/p&gt;
&lt;p&gt;I spend a lot of time in front of the computer so I made this keyboard to
improve my ergonomics. I was never able to fully switch over since I couldn’t
get over the dip in my typing speed when adapting to a new layout.&lt;/p&gt;
&lt;p&gt;Part of the reason for making a split keyboard was cost. With split layouts,
only one PCB design is required which can then be mirrored for the other half.&lt;/p&gt;
&lt;p&gt;You may recognize the PCB art.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GIS is built!&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/built.DGe5ZkES_Z1rVCBD.webp&quot; alt=&quot;gis built&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;Back of GIS&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/back.1QXdK6sL_1Qvae.webp&quot; alt=&quot;gis back&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;Front of GIS&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/front.C0TEAiRk_1FAkLq.webp&quot; alt=&quot;gis front&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Sat, 01 May 2021 00:00:00 GMT</pubDate></item><item><title>MCutie</title><link>https://enochchau.com/blog/2021/mcutie/</link><guid isPermaLink="true">https://enochchau.com/blog/2021/mcutie/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/MCutie/&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m-cutie.herokuapp.com/&quot;&gt;demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;/_astro/dashboard.CZj6aTys_Zt9YbY.webp&quot; alt=&quot;MQTT Dashboard&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;568&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;MCutie is a web app that provides logging and real time data graphs for MQTT
sensor data. Building off of Esp Temp, I’m planning to build more IoT sensor for
home automation.&lt;/p&gt;
&lt;p&gt;I wanted a dashboard so that I could log data and monitor my sensors. My initial
idea was to write a front-end client to interface with a MQTT broker websocket,
but then I wouldn’t be able to log messages.&lt;/p&gt;
&lt;p&gt;To both log messages and see real time data, I used a Raspberry Pi as a proxy
server. The Pi logs messages into a database and publish relevant messages over
a websocket. The client can also use the websocket to publish MQTT messages to
the sensors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;live websocket graph&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/graph.x_7SGzvh_2jM3eO.webp&quot; alt=&quot;Live Websocket Graph&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;576&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;In addition to the websocket, I also made a REST API for non-live data analysis
I’ve wanted to learn backend web development for some time. This project allowed
me to pick up Node, Express, and SQLite3 with Sequelize so I’d call it fairly
succesful.&lt;/p&gt;
&lt;p&gt;If you’re interested in learning more about MCutie, there’s a detailed write up
on the GitHub repository I’ve also ported the project over to PostgreSQL for a
cloud deployment on Heroku. Check out the demo!&lt;/p&gt;</description><pubDate>Mon, 01 Feb 2021 00:00:00 GMT</pubDate></item><item><title>Mosaic</title><link>https://enochchau.com/blog/2021/mosaic/</link><guid isPermaLink="true">https://enochchau.com/blog/2021/mosaic/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/mosaic&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mosaiorama.herokuapp.com/&quot;&gt;demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;/_astro/editor.CWHD2xKT_1ujj9r.webp&quot; alt=&quot;Image Editor&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;529&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Mosaic is a user based full stack web app made using MongoDB, Express.js,
Node.js and React.js. On Mosaic, you can create mosaic style images from a base
image or from randomly generated rgb pixel values.&lt;/p&gt;
&lt;p&gt;My inspiration was to make something artistic using only CSS and HTML elements.
I considered using the HTML canvas element but instead went with using div
elements because of it’s customizability with CSS&lt;/p&gt;
&lt;p&gt;In the database, the mosaics are represented as a 2D array of RGB pixel objects
with other customization data. I originally chose to use PostgreSQL but that was
restrictive on adding more customization options for the mosaics. Using MongoDB
allows me to continue to expand options and tools for the mosaics without having
to overhaul the databse.&lt;/p&gt;
&lt;p&gt;To implement users, I used JSON web tokens (JWT) to protect client side routes
and API end points from unauthorized users. Local storage and session storage
hold the JWT on the client side. Using local storage allows me to parse the JWT
to personalize content according to the current users information. I considered
using cookies to store the JWT but then I wouldn’t be able to safely access the
JWT on the client side. Also I don’t want to get sued by the EU.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;content feed&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/feed.BUs5FMD5_1fjd2u.webp&quot; alt=&quot;Content Feed&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;529&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;login&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/login.CJuj2Z6Y_7EMuy.webp&quot; alt=&quot;Login&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;529&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;post&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/post.MQkZfbFF_Z1tXXMA.webp&quot; alt=&quot;Post&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;529&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;profile&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/profile.cKV65hGf_Z1hSPh8.webp&quot; alt=&quot;Profile&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;529&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;randomizer&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/random.DmgDZ36N_Z16zdUu.webp&quot; alt=&quot;Randomizer&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;529&quot; class=&quot;image&quot;&gt; </description><pubDate>Mon, 01 Feb 2021 00:00:00 GMT</pubDate></item><item><title>Esp Temp</title><link>https://enochchau.com/blog/2021/esp-temp/</link><guid isPermaLink="true">https://enochchau.com/blog/2021/esp-temp/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/esp-temp&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;/_astro/perfboardFront.YzW54WhM_2rVsSl.webp&quot; alt=&quot;Esp Temp&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;643&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Recently, I made an IoT temperature probe. It shows data physically over a seven
segment display but it can also stream data over WiFi using MQTT.&lt;/p&gt;
&lt;p&gt;At first I didn’t really know what I could use it for, but it’s come in handy
for MQTT testing. The probe gives me an easy way to start an MQTT publisher. I
mean, I could just write a script to do that, but that’s not cool. Besides, hand
wiring is fun… right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;hand wiring is fun, right…?&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/perboardBack.Bgbg0xh7_1y3nwa.webp&quot; alt=&quot;hand wiring is fun, right...?&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;550&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;breadboarding&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/breadboard.BWz35uqN_ZyvJQj.webp&quot; alt=&quot;breadboarding&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;593&quot; class=&quot;image&quot;&gt; </description><pubDate>Fri, 01 Jan 2021 00:00:00 GMT</pubDate></item><item><title>Coffee Grams</title><link>https://enochchau.com/blog/2020/coffeegrams/</link><guid isPermaLink="true">https://enochchau.com/blog/2020/coffeegrams/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/coffee_grams&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IoT Coffee Scale PCB design and LAMP stack webapp.&lt;/p&gt;
&lt;h3 id=&quot;rev-2&quot;&gt;rev 2&lt;/h3&gt;
&lt;img src=&quot;/_astro/r2Schem.DJhBO30h_Z9xkXv.webp&quot; alt=&quot;PCB&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;339&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/r2Render.Cg9xiP2a_1EWaMx.webp&quot; alt=&quot;3-D Render of PCB&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;651&quot; class=&quot;image&quot;&gt; 
&lt;h3 id=&quot;raspberry-pi-prototype&quot;&gt;raspberry pi prototype&lt;/h3&gt;
&lt;img src=&quot;/_astro/breadboard.C3n-umGG_Z1qlCJl.webp&quot; alt=&quot;Raspberry Pi Prototype&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Tue, 01 Dec 2020 00:00:00 GMT</pubDate></item><item><title>Macro Pad</title><link>https://enochchau.com/blog/2020/macro-pad/</link><guid isPermaLink="true">https://enochchau.com/blog/2020/macro-pad/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/dropout-numpad&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;/_astro/complete.CEgST0c0_ZWAPfc.webp&quot; alt=&quot;macropad&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;799&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I can’t quite remember how I got into mechanical keyboards… but they sure are
pretty. I have to find ways to use my Electrical Engineering degree somehow, so
I figured it’d be fun to make my own keyboard. I’m currently using a 60%
keyboard and I was feeling the need for a numpad.&lt;/p&gt;
&lt;p&gt;My design uses a &lt;a href=&quot;https://www.sparkfun.com/products/12640&quot;&gt;ProMicro&lt;/a&gt; as the
controller because I wanted the whole thing to be relatively easy to hand
solder. The ProMicro is also compatible with
&lt;a href=&quot;https://github.com/qmk/qmk_firmware&quot;&gt;QMK&lt;/a&gt;, an open source keyboard firmware.&lt;/p&gt;
&lt;h3 id=&quot;rev-05&quot;&gt;Rev 0.5&lt;/h3&gt;
&lt;img src=&quot;/_astro/rev0.5.Bta_S9mT_Z1Ym0QF.webp&quot; alt=&quot;Rev 0.5&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;h3 id=&quot;rev-01&quot;&gt;Rev 0.1&lt;/h3&gt;
&lt;img src=&quot;/_astro/pretty.DG2Hkq9y_1i9BnE.webp&quot; alt=&quot;Rev 0.1 PCB w/ components&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/populated.CAkRh7_o_Z2v0YH8.webp&quot; alt=&quot;Rev 0.1 Populated PCB&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/top.Cljq7BbZ_Z1xby3E.webp&quot; alt=&quot;Rev 0.1 PCB&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;544&quot; class=&quot;image&quot;&gt; 
&lt;h3 id=&quot;rev-00&quot;&gt;Rev 0.0&lt;/h3&gt;
&lt;img src=&quot;/_astro/pcbSchematic.BuYWCtS6_ZiEFWg.webp&quot; alt=&quot;Rev 0 CAD design&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1014&quot; class=&quot;image&quot;&gt; </description><pubDate>Sun, 01 Nov 2020 00:00:00 GMT</pubDate></item><item><title>FK-2001</title><link>https://enochchau.com/blog/2020/fk-2001/</link><guid isPermaLink="true">https://enochchau.com/blog/2020/fk-2001/</guid><description>&lt;img src=&quot;/_astro/fk-0.Ck6dvum9_17HUBE.webp&quot; alt=&quot;Focus FK-2001 (Asian 102 layout)&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I found a &lt;a href=&quot;https://deskthority.net/wiki/Focus_FK-2001&quot;&gt;Focus FK-2001&lt;/a&gt; terminal
keyboard in the garage. My dad first used it in the 90’s when he started
programming. It’s honestly still pretty nice for being in storage for 10+ years.&lt;/p&gt;
&lt;p&gt;Initially, to connect the 5 pin DIN cable to my modern computer, I used a 5 pin
DIN to PS/2 adapter and a PS/2 to USB adapter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;the chain of adapters&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-5.D7Z2OroM_QChLE.webp&quot; alt=&quot;The Chain of Adapters&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;First things first, I took it apart and cleaned it throughly. I dusted out the
innards with an air duster and wiped down all the keycaps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;out of the case&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-1.DHD5DZna_ZpMeiL.webp&quot; alt=&quot;Out of the Case&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;keycaps off&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-2.C3fdSCCj_9jaCG.webp&quot; alt=&quot;Keycaps Off&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I found that some of the keys (space and left ctrl) seemed to be double
pressing. I desoldered and swapped the troublesome switches with working
switches.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;alps keycaps&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-3.gwh5eE6x_Sadnp.webp&quot; alt=&quot;Alps Keycaps&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;alps switches&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-4.Cym2UJ3V_2ViWH.webp&quot; alt=&quot;Alps style switches&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I really like the beige retro color of tech from this time period. The switches
are Alps SKCM whites or a clone, I’m not entirely sure.&lt;/p&gt;
&lt;p&gt;Since the adapter chain is bit unsightly, I decided to use a
&lt;a href=&quot;https://www.sparkfun.com/products/12640&quot;&gt;Pro-Micro&lt;/a&gt; to adapt the keyboard to
use USB.&lt;/p&gt;
&lt;p&gt;The pins of the original cable connector are labelled C, D, G, and V for clock,
data, ground, and voltage respectivley.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;C, D, G, V labels on the pcb&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-6.DXuGRIX1_83r78.webp&quot; alt=&quot;C, D, G, V labels on the pcb&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I connected these pins to the Pro-Micro’s PD0, PD1, RAW, and GND, then I flashed
the Pro-Micro with
&lt;a href=&quot;https://github.com/tmk/tmk_keyboard/tree/master/converter/ibmpc_usb&quot;&gt;Hasu’s terminal keyboard converter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;the setup&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-7.DTususq0_Z2dwIWy.webp&quot; alt=&quot;The Setup&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Luckily, there was a hole in the back of the case that was just big enough for a
USB mini port so I didn’t have to do any drilling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pro-micro in place&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-8.DRU_Ocfs_ynG2j.webp&quot; alt=&quot;Pro-Micro in Place&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;usb mini-b port&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-9.vNphtR6m_ODtlS.webp&quot; alt=&quot;USB Mini-B Port&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;I didn’t have heat shrink at the time so I hotglued the wires for insulation and
stability (don’t worry, I soldered them first).&lt;/p&gt;
&lt;p&gt;I also used a stack of cardboard and electrical tape to support the Pro-Micro in
the case. Not the prettiest solution, but no one’s going to see it inside the
case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;restored FK-2001&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/fk-10.DUylKfNY_EpxYk.webp&quot; alt=&quot;Restored FK-2001&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Thu, 01 Oct 2020 00:00:00 GMT</pubDate></item><item><title>Boulder Field</title><link>https://enochchau.com/blog/2020/boulder-field/</link><guid isPermaLink="true">https://enochchau.com/blog/2020/boulder-field/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/MP-Recs&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://enochchau.github.io/MP-Recs/index.html&quot;&gt;demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Find some bouldering projects near you. (Requires a Mountain Project account.)
This idea came about as an exercise to learn React.&lt;/p&gt;
&lt;img src=&quot;/_astro/data.BktyPzn__gbGcP.webp&quot; alt=&quot;Bouldering Suggestions&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;797&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/form.DfPNUwsV_1gxEB4.webp&quot; alt=&quot;Form&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;780&quot; class=&quot;image&quot;&gt; </description><pubDate>Sat, 01 Aug 2020 00:00:00 GMT</pubDate></item><item><title>Projector Buddy</title><link>https://enochchau.com/blog/2020/projector-buddy/</link><guid isPermaLink="true">https://enochchau.com/blog/2020/projector-buddy/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://calplug.org/&quot;&gt;calplug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.calit2.uci.edu/&quot;&gt;calit2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Projector Buddy is an advanced energy system that controls a projectors
electricity usage based on human presence in the room. Human activity is
determined through light, sound, and motion. In order to preserve the projectors
internal electronics and bulb, a soft shutdown is initiated using an IR blaster.&lt;/p&gt;
&lt;p&gt;My primary contribution was firmware development. I implemented freeRTOS
firmware, updated the control algorithm, and created IR Receiver and IR Emitter
functionality.&lt;/p&gt;
&lt;img src=&quot;/_astro/topV2.u4pr3vKG_2mjpaH.webp&quot; alt=&quot;topdown view&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;946&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/side.DwJksbTY_kPJnN.webp&quot; alt=&quot;side view&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Mon, 01 Jun 2020 00:00:00 GMT</pubDate></item><item><title>Water Cooler Buddy</title><link>https://enochchau.com/blog/2020/wcb/</link><guid isPermaLink="true">https://enochchau.com/blog/2020/wcb/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://calplug.org/&quot;&gt;calplug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.calit2.uci.edu/&quot;&gt;calit2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Water Cooler Buddy is an energy management system for a water cooler. Water
Cooler Buddy controls it’s sensors, relays, and user interface using an ESP32
microcontroller.&lt;/p&gt;
&lt;p&gt;My primary contribution was firmware development. I updated firmware for ESP32
implementation and created an RTOS scheduling system. I also assisted with PCB
population and water cooler wiring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;main and interface PCB&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/pcb.DgHtGCk-_Z6ucFT.webp&quot; alt=&quot;Main and Interface PCB&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;683&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;watercooler&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/outside.BHYE4qIP_1voLYD.webp&quot; alt=&quot;Watercooler&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;demo user interface&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/front.tLvkkSNJ_ZEBdsD.webp&quot; alt=&quot;Demo User Interface&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;1067&quot; class=&quot;image&quot;&gt; </description><pubDate>Mon, 01 Jun 2020 00:00:00 GMT</pubDate></item><item><title>GPS Sound Sensor</title><link>https://enochchau.com/blog/2020/gpsss/</link><guid isPermaLink="true">https://enochchau.com/blog/2020/gpsss/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://projects.eng.uci.edu/projects/2019-2020/gps-sound-sensor-rescue-assistance&quot;&gt;project site&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The GPSSS is a WiFi connected directional sensor based on the time delay of
arrival algorthim for sound. My contributions include configuring WiFi
connectivity between the sensor and android application. Special thanks to my
teammates Minh Tam Chau, Sam Longo, and Kelwin Li.&lt;/p&gt;
&lt;video width=&quot;640&quot; controls&gt;&lt;source src=&quot;/videos/gpsss_vid.mp4&quot; type=&quot;video/mp4&quot;/&gt;&lt;p&gt;Your browser does not support the video tag.&lt;/p&gt;&lt;/video&gt;
&lt;p&gt;&lt;strong&gt;GPSSS&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/1.mERu-br6_Zc68eH.webp&quot; alt=&quot;GPSSS&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;&lt;strong&gt;sensor internals&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/2.00xhoVYZ_1ScQbX.webp&quot; alt=&quot;Sensor Internals&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Sun, 01 Mar 2020 00:00:00 GMT</pubDate></item><item><title>HC2 IoT Sensor</title><link>https://enochchau.com/blog/2019/hc2/</link><guid isPermaLink="true">https://enochchau.com/blog/2019/hc2/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/HC2_arduino_python&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I created firmware libraries in Python (for Raspberry Pi) and Arduino to
interface with the industry standard Rotronics HC2 temperature and humidity
sensor. I also designed a prototype IoT network for the HC2 sensor (currently a
private repository). Connections include WiFi, Email, and LoRa.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HC2, Moteino (LoRa), &amp;amp; Raspberry Pi&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/hc2.3b3JOLAC_ZtHRtj.webp&quot; alt=&quot;HC2, Moteino (LoRa), &amp;#38; Raspberry Pi&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Fri, 01 Nov 2019 00:00:00 GMT</pubDate></item><item><title>Scale Up</title><link>https://enochchau.com/blog/2019/scaleup/</link><guid isPermaLink="true">https://enochchau.com/blog/2019/scaleup/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/smart_scale&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devpost.com/software/scale-up&quot;&gt;devpost&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/G3OunuLPnXA&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;Scale Up can calculate caloric data based on weight of foods. Google Vision was
used to find food types while the USDA Nutrition API to find caloric
information. Checkout the DevPost for more infromation.&lt;/p&gt;
&lt;img src=&quot;/_astro/labeled.Cw6-94cp_B7z24.webp&quot; alt=&quot;Scale Up&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Tue, 01 Oct 2019 00:00:00 GMT</pubDate></item><item><title>3 Phase Wattmeter</title><link>https://enochchau.com/blog/2019/3phase/</link><guid isPermaLink="true">https://enochchau.com/blog/2019/3phase/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/CalPlug/ADE9078-3PhaseWattmeter&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://calplug.org/&quot;&gt;calplug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.calit2.uci.edu/&quot;&gt;calit2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;demo setup&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/demo.dT0QBtln_DgxNn.webp&quot; alt=&quot;Demo Setup&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;380&quot; class=&quot;image&quot;&gt; 
&lt;p&gt;Using the ADE9078, our group created a PCB and wrote firmware to create a small
portable three phase wattmeter. Our demo setup was designed to detect faults in
a three phase motor. By performing a Fast Fourier Transform (FFT) on the power
data and looking for irregularities, we were able to determine if the motor was
faulty.&lt;/p&gt;
&lt;p&gt;My primary contribution was configuring the waveform buffer firmware and
creating a graphical interface for FFT.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;power graph&lt;/strong&gt;&lt;/p&gt;
&lt;video width=&quot;640&quot; controls&gt;&lt;source src=&quot;/videos/3phasewatt_sin.mov&quot; type=&quot;video/mp4&quot;/&gt;&lt;p&gt;Your browser does not support the video tag.&lt;/p&gt;&lt;/video&gt;
&lt;p&gt;&lt;strong&gt;FFT processing graph&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;/_astro/processing.C2GUh1AG_wUku3.webp&quot; alt=&quot;FFT Processing Graph&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;444&quot; class=&quot;image&quot;&gt; </description><pubDate>Sat, 01 Jun 2019 00:00:00 GMT</pubDate></item><item><title>MicroMouse</title><link>https://enochchau.com/blog/2019/micromouse/</link><guid isPermaLink="true">https://enochchau.com/blog/2019/micromouse/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/micromouse_2019&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MicroMouse is a world wide competition in the promotion of autonomous vehicles.
Using IR transceivers to detect distance from walls, the mouse can navigate
through a maze using it’s microcontroller “brain”.&lt;/p&gt;
&lt;img src=&quot;/_astro/pretty.CszkBs6R_29Qbjc.webp&quot; alt=&quot;micromouse&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/top.D4zeMGHT_Z1fangk.webp&quot; alt=&quot;top&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;677&quot; class=&quot;image&quot;&gt; </description><pubDate>Fri, 01 Mar 2019 00:00:00 GMT</pubDate></item><item><title>The C-ing Hat</title><link>https://enochchau.com/blog/2019/cing-hat/</link><guid isPermaLink="true">https://enochchau.com/blog/2019/cing-hat/</guid><description>&lt;h1 id=&quot;the-c-ing-hat&quot;&gt;The C-ing Hat&lt;/h1&gt;
&lt;h2 id=&quot;february-2020&quot;&gt;February 2020&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/enochchau/the_all_C-ing_eye&quot;&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devpost.com/software/the-c-ing-hat&quot;&gt;devpost&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A prototype hat for object detection. Checkout the DevPost for more information.&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/SFQxsU8La4k&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;img src=&quot;/_astro/side.D7QSW2fj_26b5vx.webp&quot; alt=&quot;side&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; 
&lt;img src=&quot;/_astro/top.CeEr8rrB_EFazD.webp&quot; alt=&quot;topdown&quot; data-astro-cid-snomllr7=&quot;true&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;image&quot;&gt; </description><pubDate>Fri, 01 Feb 2019 00:00:00 GMT</pubDate></item></channel></rss>