[{"data":1,"prerenderedAt":129},["ShallowReactive",2],{"journal-\u002Fjournal\u002Fshipping-a-tiny-mac-app":3},{"id":4,"title":5,"body":6,"date":115,"description":116,"draft":117,"extension":118,"meta":119,"navigation":120,"path":121,"seo":122,"stem":123,"tags":124,"__hash__":128},"journal\u002Fjournal\u002Fshipping-a-tiny-mac-app.md","Shipping a Tiny Mac App, and Everything I Cut to Do It",{"type":7,"value":8,"toc":107},"minimark",[9,13,16,21,24,27,30,38,42,45,52,63,66,70,73,76,80,83,86,89,92,95,98,101,104],[10,11,12],"p",{},"I built a small macOS app for one person with one problem. A tech lead running several AI agents at once, who wanted a checklist permanently visible without giving up screen space or alt-tabbing to find it.",[10,14,15],{},"That's the whole brief. Everything about the app follows from it, especially the things it doesn't do.",[17,18,20],"h2",{"id":19},"a-bookmark-on-the-edge-of-your-screen","A bookmark on the edge of your screen",[10,22,23],{},"The app docks flush to a screen edge as a full-height strip. Expanded it's a 300-pixel panel you can type in. Collapsed it's a 30-pixel ribbon with the title rotated vertically along it, like a bookmark sticking out of a book. A chevron toggles between them.",[10,25,26],{},"That's the design idea worth stealing. An always-on-top window is useful and annoying in roughly equal measure. You want the information, you don't want a rectangle sitting on your work. Collapsing to a labelled ribbon keeps it glanceable while giving back almost all the space.",[10,28,29],{},"The fiddly bit is that collapse and expand have to be exactly reversible. Before collapsing, the app captures the current window rectangle so expanding restores the same size and position instead of a guess. Get it wrong and the window creeps a few pixels every cycle, which makes an app feel cheap in a way nobody can quite articulate.",[10,31,32,33,37],{},"Positioning computes from the current monitor's geometry, which means dealing with logical versus physical pixels. Divide by the scale factor, or your carefully calculated edge position lands halfway across a Retina display. State goes to ",[34,35,36],"code",{},"localStorage",", so it comes back where you left it.",[17,39,41],{"id":40},"two-tauri-v2-gotchas-that-cost-an-evening","Two Tauri v2 gotchas that cost an evening",[10,43,44],{},"Both are the kind where the code looks right and the error doesn't help.",[10,46,47,48,51],{},"Size and position arguments need a type discriminator. The window methods want an object carrying an explicit ",[34,49,50],{},"{ type: 'Logical', ... }"," tag. The nested shape you'd reasonably infer from older examples throws instead of working.",[10,53,54,55,58,59,62],{},"Monitor functions are module-level, not window methods. ",[34,56,57],{},"currentMonitor()"," and ",[34,60,61],{},"primaryMonitor()"," are imported functions, not methods on the window object. Go looking for them on the instance and you'll conclude they don't exist.",[10,64,65],{},"You also need matching permissions declared explicitly. Setting size, setting position, reading outer size and position, querying monitors, each separately permissioned. Tauri v2's capability model is a real improvement over blanket access, but a missing entry presents as a method that silently does nothing.",[17,67,69],{"id":68},"no-build-step","No build step",[10,71,72],{},"The app serves its source directory directly. No bundler, no dev server, no compile-on-save.",[10,74,75],{},"The tradeoff is using the framework's global object instead of module imports, which needs a config flag. In exchange the entire frontend toolchain is \"the files are the app\". For something this small that's the right trade. A build pipeline would be bigger than the thing it builds, and every dependency in it becomes a future upgrade I'd have to care about for an app whose feature list is finished.",[17,77,79],{"id":78},"what-i-cut","What I cut",[10,81,82],{},"This is the part worth reading.",[10,84,85],{},"No multiple notes. The obvious next feature, requested by nobody who was going to use it.",[10,87,88],{},"No sync, no accounts, no cloud. All local. Sync meant a backend, auth, conflict resolution and a privacy story, for a note living on one machine belonging to one person.",[10,90,91],{},"No structured to-do items. I built a version with proper checkbox handling and threw it away. The content is now one editable region persisted as-is. Structured items meant decisions about nesting, ordering, completion state and reordering. A week of work to be slightly better than typing a dash.",[10,93,94],{},"No window chrome. Frameless, no traffic lights, a custom close button as the only exit. Real usability cost, deliberately accepted, because a title bar on a 30-pixel ribbon is absurd.",[10,96,97],{},"No Dock icon. It lives in the menu bar. It isn't an app you switch to, it's an app that's just there.",[10,99,100],{},"The one indulgence is eight colour themes, done as CSS variable overrides on a data attribute. Cheap, and the app is on screen constantly, so being able to make it not-ugly-to-you is worth more than it would be in software you look at twice a day.",[10,102,103],{},"Docking a window to a screen edge is not hard. The value here isn't technical. It's that the thing is finished, has a specific user, an installable build, and a list of refusals longer than its list of features.",[10,105,106],{},"I have several ambitious side projects that don't exist. This one took an evening or two and gets used. The difference was entirely in the scoping.",{"title":108,"searchDepth":109,"depth":109,"links":110},"",2,[111,112,113,114],{"id":19,"depth":109,"text":20},{"id":40,"depth":109,"text":41},{"id":68,"depth":109,"text":69},{"id":78,"depth":109,"text":79},"2026-07-19","A sticky note that docks to the edge of your screen, built in Tauri v2 with no build step. The interesting part wasn't what went in. It was the list of obvious features I refused.",false,"md",{},true,"\u002Fjournal\u002Fshipping-a-tiny-mac-app",{"title":5,"description":116},"journal\u002Fshipping-a-tiny-mac-app",[125,126,127],"engineering","tauri","macos","SpK0Xgw3HWxfQ1Rm4CXRnDIncv6THDxR3B_BtmESpDw",1787915616666]