You are browsing as a guest. Sign up (or log in) to start making projects!

2h 52m 43s logged

Devlog #2

Building my portfolio as an operating system that runs in a browser tab.

Picking up from Devlog #1, where the desktop finally had bones — windows you
could drag, focus, resize, and minimize — but nothing around them. An empty
wallpaper with floating rectangles isn’t an OS; it’s a screensaver. This
milestone is about the two pieces of chrome that make it read as a real desktop
the instant it loads: the menu bar across the top and the dock along the
bottom.

A menu bar

The menu bar is mostly static — a logo that drops open into an “About / Restart”
menu, and the usual File / Edit / View / Help labels that don’t do much yet. The
one part that has to feel alive is the clock in the top-right corner. A frozen
clock immediately breaks the illusion, so it ticks once a second

Nothing fancy — a setInterval that recomputes the formatted string every
second and clears itself on unmount. The formatting is where I tripped.

The crash that ate the whole screen

I wanted the clock in Korean — “7월 26일 (일) 오후 11:24” — so I reached for the
browser’s built-in localization instead of hand-formatting dates:

const date = d.toLocaleDateString("ko-KR", { month: "short", day: "numeric", weekday: "short" })
const time = d.toLocaleTimeString("ko_KR", { hour: "2-digit", minute: "2-digit" })

And the app went white. Not a subtly-wrong date — the entire MenuBar component
threw and took the tree down with it:

Uncaught RangeError: invalid language tag: "ko_KR"
    formatNow  MenuBar.tsx:74

It took me an embarrassing minute because the two lines look identical at a
glance. Look again: the date says ko-KR, the time says ko_KR. A hyphen on one
line, an underscore on the next.

The fix was one keystroke — underscore to hyphen — and the shell came back.

Lesson filed away: locale tags are hyphenated, always, and Intl validates them
strictly enough to crash. Two things to fix properly later — normalize the locale
in one constant so I can’t typo it twice, and wrap the shell in an error boundary
so a single throw dims one widget instead of nuking the screen.

The dock

The dock is the fun part. I wanted the macOS-style effect where icons swell as
the cursor passes over them and the neighbors ease up too, so it feels like the
row is being pushed under a magnifying glass rather than icons popping one at a
time.

On a trackpad-less touch screen there’s no hover state, so a finger tap would
otherwise make the whole dock lurch to wherever you touched. Gating on
pointerType === "mouse" keeps touch calm and leaves the magnification as a
mouse-only flourish, which is exactly where it belongs.

Where stands

  • Menu bar with a working logo menu and a live, once-a-second clock.
  • Clock is localized through Intl — after learning the hard way that the tag
    must be ko-KR, not ko_KR.
  • Dock with smooth Gaussian cursor magnification and a subtle lift, mouse-only.
  • The desktop finally reads as a desktop at first glance, even before any real
    apps exist.

Next up

The apps themselves — About and Projects as real windows wired into the window
manager, replacing the placeholder stand-ins the dock currently launches. And,
not incidentally, the boot sequence needs a proper redesign; the current one is
placeholder-grade and doesn’t match the polish of the shell it opens into.

0
22

Comments 1

@water

peak project twin!! keep it going!