<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: crazyathlete220-stack</title>
    <description>The latest articles on DEV Community by crazyathlete220-stack (@crazyathlete220stack).</description>
    <link>https://dev.to/crazyathlete220stack</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3967895%2F067d6a44-5341-486e-8073-402143f6207e.png</url>
      <title>DEV Community: crazyathlete220-stack</title>
      <link>https://dev.to/crazyathlete220stack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/crazyathlete220stack"/>
    <language>en</language>
    <item>
      <title>I outlined every window in its own color so I can tell my apps apart</title>
      <dc:creator>crazyathlete220-stack</dc:creator>
      <pubDate>Thu, 04 Jun 2026 14:57:05 +0000</pubDate>
      <link>https://dev.to/crazyathlete220stack/i-outlined-every-window-in-its-own-color-so-i-can-tell-my-apps-apart-4je0</link>
      <guid>https://dev.to/crazyathlete220stack/i-outlined-every-window-in-its-own-color-so-i-can-tell-my-apps-apart-4je0</guid>
      <description>&lt;p&gt;When I have a lot of apps open, I lose track of which window is which.&lt;/p&gt;

&lt;p&gt;These days everything has the same dark UI — the terminal, the editor, the chat&lt;br&gt;
app all look alike at a glance. I kept opening Mission Control just to find the&lt;br&gt;
window I wanted, and it quietly added up.&lt;/p&gt;

&lt;p&gt;So I gave each app its own color. Pick a color per app, outline that app's&lt;br&gt;
windows in it, and you can recognize them at a glance — blue is this, red is&lt;br&gt;
that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8hf32ot9tj95k9pf9fk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8hf32ot9tj95k9pf9fk.png" alt="window-border — each app gets its own outline color" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/crazyathlete220-stack/window-border" rel="noopener noreferrer"&gt;https://github.com/crazyathlete220-stack/window-border&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;It's a small menu-bar app. Under the hood it just asks &lt;code&gt;CGWindowList&lt;/code&gt; for the&lt;br&gt;
on-screen windows — their position and which app owns them — and draws a colored&lt;br&gt;
stroke along each window's frame. Because it only reads geometry (not pixels), it&lt;br&gt;
needs &lt;strong&gt;no screen-recording permission&lt;/strong&gt;. The outlines are transparent and&lt;br&gt;
click-through, so they never get in your way.&lt;/p&gt;

&lt;p&gt;Colors are auto-assigned per app, but you can pin them in&lt;br&gt;
&lt;code&gt;~/.config/window-border/colors.json&lt;/code&gt; if you want "Safari is always blue."&lt;/p&gt;

&lt;h2&gt;
  
  
  The fiddly parts
&lt;/h2&gt;

&lt;p&gt;It looks trivial, but it took a few tries to behave.&lt;/p&gt;

&lt;p&gt;First, the outline sat slightly off from the window. macOS was "helpfully"&lt;br&gt;
shifting my borderless overlay to keep it on screen; turning that off fixed it.&lt;/p&gt;

&lt;p&gt;Then, dragging a window fast made the outline disappear behind it. The outline is&lt;br&gt;
a separate window, so when the dragged window jumps to the front it covers the&lt;br&gt;
outline. The fix is to lift the outline back on top whenever something moves.&lt;/p&gt;

&lt;p&gt;Finally, the active window's outline kept hiding — every click or keystroke&lt;br&gt;
raises that window above its outline. So the frontmost window's outline gets&lt;br&gt;
re-lifted every frame.&lt;/p&gt;

&lt;p&gt;Doing all of that every frame would be heavy, so it only re-stacks when something&lt;br&gt;
moves (plus the frontmost window). When the screen is still, it does nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limit
&lt;/h2&gt;

&lt;p&gt;The outline &lt;em&gt;chases&lt;/em&gt; the window, so a very fast flick leaves a one-frame trail.&lt;br&gt;
That can't go to zero — you can't physically attach an overlay to another app's&lt;br&gt;
window.&lt;/p&gt;

&lt;p&gt;No private APIs, MIT licensed. Color-coding turned out to be more comfortable&lt;br&gt;
than I expected. If you also keep asking "wait, which window was that," here you&lt;br&gt;
go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/crazyathlete220-stack/window-border" rel="noopener noreferrer"&gt;https://github.com/crazyathlete220-stack/window-border&lt;/a&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>objectivec</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>ytmeta: bulk-edit YouTube metadata from the CLI, without the fear</title>
      <dc:creator>crazyathlete220-stack</dc:creator>
      <pubDate>Thu, 04 Jun 2026 09:47:49 +0000</pubDate>
      <link>https://dev.to/crazyathlete220stack/ytmeta-bulk-edit-youtube-metadata-from-the-cli-without-the-fear-17gc</link>
      <guid>https://dev.to/crazyathlete220stack/ytmeta-bulk-edit-youtube-metadata-from-the-cli-without-the-fear-17gc</guid>
      <description>&lt;p&gt;If you run a YouTube channel with more than a handful of videos, some chores are&lt;br&gt;
quietly miserable. Appending the same footer to every description. Renaming&lt;br&gt;
titles to a new convention. Dropping a pinned-style comment on your top videos.&lt;br&gt;
YouTube Studio makes you do all of this one video at a time.&lt;/p&gt;

&lt;p&gt;So you write a script. And now you have a worse problem: a bulk &lt;code&gt;videos.update&lt;/code&gt;&lt;br&gt;
loop will happily overwrite dozens of descriptions if you get one line wrong, and&lt;br&gt;
there's no undo. I wanted the bulk part without that sinking feeling.&lt;/p&gt;

&lt;p&gt;That's the whole idea behind &lt;a href="https://github.com/crazyathlete220-stack/ytmeta" rel="noopener noreferrer"&gt;ytmeta&lt;/a&gt;:&lt;br&gt;
&lt;strong&gt;every write is a dry-run until you explicitly say otherwise.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; ytmeta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# preview exactly what would change (reads the API, changes nothing)&lt;/span&gt;
ytmeta titles titles.json

&lt;span class="c"&gt;# apply it once you're happy&lt;/span&gt;
ytmeta titles titles.json &lt;span class="nt"&gt;--execute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few decisions follow from that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every write command previews first; nothing changes without &lt;code&gt;--execute&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;uploads default to &lt;code&gt;private&lt;/code&gt;, so nothing goes public by accident&lt;/li&gt;
&lt;li&gt;deletes need a typed confirmation phrase, even with &lt;code&gt;--execute&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;your &lt;code&gt;client_secret.json&lt;/code&gt; and &lt;code&gt;token.json&lt;/code&gt; stay local and are never committed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A real example I use a lot — add a footer to a bunch of videos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VIDEO_ID_1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"append"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;—&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Subscribe: https://youtube.com/@you"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VIDEO_ID_2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"append"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;—&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Subscribe: https://youtube.com/@you"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ytmeta descriptions data.json &lt;span class="nt"&gt;--execute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry can &lt;code&gt;append&lt;/code&gt;, &lt;code&gt;prepend&lt;/code&gt;, or fully replace the description.&lt;/p&gt;

&lt;p&gt;The rest of the commands: &lt;code&gt;login&lt;/code&gt; (OAuth loopback), &lt;code&gt;titles&lt;/code&gt;, &lt;code&gt;comments&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;watermark&lt;/code&gt;, &lt;code&gt;upload&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's a thin wrapper over the YouTube Data API — Node 18+, tested with the&lt;br&gt;
built-in &lt;code&gt;node:test&lt;/code&gt;, CI on Node 18/20/22, MIT. If it's useful to you I'd love to&lt;br&gt;
hear what to add next.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/crazyathlete220-stack/ytmeta" rel="noopener noreferrer"&gt;https://github.com/crazyathlete220-stack/ytmeta&lt;/a&gt;&lt;/p&gt;

</description>
      <category>youtube</category>
      <category>cli</category>
      <category>node</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I turned a 2011 iMac into a second display by faking a monitor in macOS</title>
      <dc:creator>crazyathlete220-stack</dc:creator>
      <pubDate>Thu, 04 Jun 2026 08:41:27 +0000</pubDate>
      <link>https://dev.to/crazyathlete220stack/i-turned-a-2011-imac-into-a-second-display-by-faking-a-monitor-in-macos-2db5</link>
      <guid>https://dev.to/crazyathlete220stack/i-turned-a-2011-imac-into-a-second-display-by-faking-a-monitor-in-macos-2db5</guid>
      <description>&lt;p&gt;I have a 2011 iMac I can't bring myself to throw away. I just like the way it looks. But a machine sitting in a closet is a waste, so I figured I'd at least make it a second screen for my main Mac.&lt;/p&gt;

&lt;p&gt;Turns out that's harder than it sounds.&lt;/p&gt;

&lt;p&gt;The 2011 iMac has no video input, so it can't be an HDMI monitor. Apple's Target Display Mode looks like the answer, but the model/OS requirements rule out my current Mac. VNC / Screen Sharing does work, but the lag makes it useless as an actual display — a monitor that stutters every time you move the mouse is not a monitor I want.&lt;/p&gt;

&lt;p&gt;So the normal routes were all dead ends.&lt;/p&gt;

&lt;h2&gt;
  
  
  Faking a display instead
&lt;/h2&gt;

&lt;p&gt;I gave up on connecting anything physically and went the other way: &lt;strong&gt;create a display inside macOS that doesn't exist.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;macOS has a private API, &lt;code&gt;CGVirtualDisplay&lt;/code&gt;, that lets you spin up a screen the OS treats as a real external monitor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight objective_c"&gt;&lt;code&gt;&lt;span class="k"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;CGVirtualDisplay&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NSObject&lt;/span&gt;
&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;initWithDescriptor&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BOOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;applySettings&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that virtual display exists, the only job left is getting its contents onto the old iMac. The setup I landed on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main Mac
  oldmac-display   -&amp;gt; creates the virtual display (CGVirtualDisplay)
  Sunshine         -&amp;gt; streams that display with low latency
        |
        v
2011 iMac (macOS 10.13)
  Moonlight        -&amp;gt; receives it fullscreen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A display called &lt;strong&gt;Old Mac Display&lt;/strong&gt; shows up in System Settings, and I arrange windows on it like any external monitor. It's just being mirrored to the iMac.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that bit me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The display can get stranded.&lt;/strong&gt; Place the virtual display wrong and windows fly off to a screen you can't see. So by default it quits after 45 seconds; once you trust it, run with &lt;code&gt;--keep-alive&lt;/code&gt;. &lt;code&gt;pkill -f oldmac-display&lt;/code&gt; kills it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VNC really is too slow.&lt;/strong&gt; Switching to Sunshine + Moonlight (the game-streaming stack) is what made it actually usable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Old macOS can't run new Moonlight.&lt;/strong&gt; On the iMac's 10.13 the latest Moonlight won't launch; you need an older build. It's in the README so nobody else loses an hour to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveat
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CGVirtualDisplay&lt;/code&gt; is a private API, so a future macOS could break it. I'm treating this as a "fun while it lasts" project. The virtual-display part is also handy beyond old Macs — headless setups, custom-resolution scratch screens, etc.&lt;/p&gt;

&lt;p&gt;It works well enough, and honestly I'm just happy the iMac I like is useful again. MIT licensed if you've got an old Mac gathering dust too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/crazyathlete220-stack/oldmac-display" rel="noopener noreferrer"&gt;https://github.com/crazyathlete220-stack/oldmac-display&lt;/a&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>objectivec</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
