# zrosenbauer.com — full corpus > Full Markdown corpus for LLM consumption. Generated from the site content at build time. index: https://zrosenbauer.com/llms.txt --- # about source: https://zrosenbauer.com/gui/about _the long version — where i’ve worked, what i’ve shipped, and what i keep coming back to._ ## 👋 Hi, I'm Zac Rosenbauer. I've spent over a decade working in startups as a software engineer, manager, and executive. I've been fortunate to work with some amazing people and build some great products. I've also made a lot of mistakes along the way. My hope is that through my blog I can share some of the things I've learned and help others avoid some of the mistakes I've made, or at least make new ones. 😅 ## The Road so Far I started out my career by starting a consulting agency, [PointStart](https://pointstart.io), (while still in college) that built websites and e-commerce integrations aka lots of Wordpress sites & stores. After PointStart shut down, because I didn't enjoy building Wordpress sites, I joined [neighborhoods.com](https://neighborhoods.com) as the 2nd engineer, to build out their real estate platform. I took my first stab at the pre-seed, super early startup world with [Precognitive](https://www.crunchbase.com/organization/precognitive-inc) (acquired by ShopRunner) as the VP of Engineering. After we were acquired, I joined [ShopRunner](https://shoprunner.com) as the Head of Platform Engineering, where I helped build out their platform engineering (aka DevOps) and fraud detection systems (cause of Precognitive). ShopRunner was acquired by FedEx in 2020 and I moved to a Director of Platform Engineering at [FedEx Dataworks](https://www.fedex.com/en-us/dataworks.html) where I ran an organization of 100+ engineers. After a year at FedEx, I decided to leave and join a startup again, but this time I wanted to **start** the start-up (not intending to be punny). I started Joggr with my co-founder, [Seth](https://github.com/srosenbauer), in late 2022. ## What I'm up to now Shipping open-source and writing here. Mostly TypeScript, Node, and Rust day-to-day, but I'm a purveyor of all languages and will reach for whatever fits the job. Previously: I co-founded [Joggr](https://joggr.ai) with [Seth](https://github.com/srosenbauer) in late 2022 — the developer toolkit for building with AI agents. We gave engineering teams primitives for shipping AI-agent features that actually worked: context, evals, and the glue between codebase and model. We wound the company down in 2026. ## The personal stuff I live in New York City with my wife and dog, I train mixed martial arts and self defense systems at least 2-3 times a week, and I try to travel as much as I can. If you want to get to know the country side of me (I was born and raised in rural Ohio 🌽) just ask me about tractor pulls. If you want to chat about startups, software engineering, or just want to say hi, feel free to reach out to [me](/gui/contact). --- # how rust ruined javascript: result source: https://zrosenbauer.com/gui/blog/posts/rust-ruined-javascript-result _2026-05-20 · 4 min · @zrosenbauer · tags: typescript, rust, dx_ [Last post](/gui/blog/posts/rust-ruined-javascript-match) I admitted that Rust's `match` ruined `switch` for me. This one is about the second casualty: `try/catch`. I've written functional JS for a long time, the kind of code that returns values instead of throwing them, but TypeScript has never had a real `Result` so exceptions kept seeping back into my codebases. Rust gave the missing piece a name. So I built it into my own toolkit. ## 📝 the background TypeScript's error story is "throw whatever you want, catch it as `unknown`, hope you remember to handle it". A function signature looks like this: ```ts function readConfig(): Config { // somewhere in here, anything could throw } ``` Nothing on the type tells me this can fail. Nothing tells me what it fails with. The caller has to know, from context or a comment or a stack trace at 3am, that this function reaches out to disk and could throw an `ENOENT`, or a `SyntaxError` on bad JSON, or a `TypeError` from a missing field, or something a dependency three levels down decided to throw without telling anyone. The TypeScript answer is `try/catch` plus the discipline to remember every layer where it matters: ```ts try { const config = readConfig(); use(config); } catch (thrown) { // `thrown` is `unknown` here, by design, since TS 4.4 if (thrown instanceof Error) { log(thrown.message); } else { log(String(thrown)); } } ``` That's the "good" version. Narrow on `instanceof`, no `any` smuggled in. It also costs five lines around every fallible call, and the moment a function I called yesterday starts throwing something new, none of my existing call sites learn about it. The compiler shrugs. The runtime breaks. ## 🤯 the discovery Rust doesn't throw for recoverable errors. A function that can fail says so in its return type: ```rust fn read_config() -> Result { // ... } fn main() -> Result<(), ConfigError> { let config = read_config()?; use_config(config); Ok(()) } ``` Three things hit me at once the first time I really sat with it: 1. The failure is in the signature. `Result` tells me, before I read a single line of the body, that this can fail and what shape the failure has. 2. The compiler won't let me ignore it. I have to match on it, unwrap it, or propagate it. There's no silent fall-through, no forgotten `try` block, no error vanishing into the void. 3. `?` is the entire ergonomic story. Propagate the error one character at a time. If you want to handle it, fall out of `?` and `match` on it. The thing I'd been simulating in TypeScript out of `try/catch` plus `instanceof Error` plus convention is a first-class feature in Rust, enforced by the type system from the function signature down. Not a pattern. Not an idiom. Actual semantics. > [!TIP] > The failure mode in the signature is the feature. The `?` operator is the bonus. ## 🔧 building it myself My JS has always leaned functional. For years that meant [`ramda`](https://ramdajs.com/), which carried me through a lot of code before it effectively went unmaintained. When [`es-toolkit`](https://es-toolkit.dev/) showed up I moved over, and it's been my default helper library since. Both are great. Neither has a `Result`. The TypeScript ecosystem has takes on this one piece in isolation. [`neverthrow`](https://github.com/supermacro/neverthrow) is the most popular, and it's good. [`ts-results`](https://github.com/vultix/ts-results) and [`oxide.ts`](https://github.com/traverse1984/oxide.ts) exist. So does [`effect`](https://effect.website/) if you want the whole functional kitchen sink. I tried them. They all do something I don't want. `neverthrow` is class-based with a fluent chain. `effect` is its own programming model. `oxide.ts` is closer, but the API surface kept pulling me into things I didn't need. None of them gave me a plain discriminated union I can destructure, narrow with a type guard, and unwrap when I'm ready to deal with it. And none of them live next to the rest of the functional primitives I reach for every day. So I built one. It lives in a library called [`massaman`](https://www.npmjs.com/package/massaman) ([source on GitHub](https://github.com/zrosenbauer/massaman)), my "perfect-ish" successor to ramda and es-toolkit for the way I actually write JS. Currently shipping under the `rc` tag at `0.0.1-rc.2`, 177 tests, 100% coverage. The whole `Result` module is one file: ```ts import { isNil } from 'es-toolkit/predicate'; import type { Err, Ok, Result } from './types.js'; function coerceError(thrown: unknown): Error { if (thrown instanceof Error) return thrown; if (typeof thrown === 'string') return new Error(thrown); try { const message = JSON.stringify(thrown) ?? String(thrown); return new Error(message, { cause: thrown }); } catch { return new Error(String(thrown), { cause: thrown }); } } export function ok(value: T): Ok { return { ok: true, value, error: null }; } export function err(error: unknown): Err { return { ok: false, value: null, error: coerceError(error) }; } export function isOk(result: Result): result is Ok { return result.ok === true; } export function isErr(result: Result): result is Err { return result.ok === false; } export function unwrap(result: Result, message?: string): T { if (result.ok) return result.value; if (!isNil(message)) throw new Error(message, { cause: result.error }); throw result.error; } ``` The types are the boring part, but worth showing so the rest reads: ```ts export type Ok = { ok: true; value: T; error: null }; export type Err = { ok: false; value: null; error: Error }; export type Result = Ok | Err; ``` A few choices worth calling out, since this is the part I cared about: - **Plain object, no class.** The result is a discriminated union on `ok: true | false`. You can destructure it, log it, `JSON.stringify` it, send it across a worker boundary. No prototype, no `instanceof`, no `Result.fromPromise` ritual. - **`error` is always an `Error`.** If you call `err('boom')` or `err(42)`, `coerceError` wraps it. The original value is preserved on `cause`. The rest of my code never has to write `if (thrown instanceof Error)` again. - **Single-arg generic.** Rust's `Result` parameterizes both sides. Mine is `Result` because in TypeScript the error side is almost always "an `Error`, somehow", and the `cause` chain plus `Error` subclasses cover the rest. I traded the generic E for a coercion the caller never has to think about. - **`unwrap` mirrors Rust.** Call it with no args to rethrow the original error. Call it with a message to throw a new error with the original as `cause`. That's `Result::expect` in Rust, and it covers 90% of the cases where I actually want to bail. - **Type guards instead of methods.** `isOk(result)` and `isErr(result)` narrow the union. The arms become regular `if` blocks, not a `.map().mapErr().match()` chain. A typical call site, with `attempt` wrapping the throwing API at the boundary so I never write `try/catch` in my own code: ```ts import { attempt, isErr } from 'massaman'; function readConfig(): Result { return attempt(() => { const raw = fs.readFileSync('./config.json', 'utf8'); return JSON.parse(raw) as Config; }); } const result = readConfig(); if (isErr(result)) { log(result.error.message); return; } use(result.value); ``` That's the whole API surface that matters. Five functions and three types. No chain, no class, no framework. ## 🤷 why not one of the existing ones? I tried all of them. Half are effectively abandoned, the rest don't match how I write TS. - **`neverthrow`** — the most popular pick, and the one I respect the most. Class-based with a fluent chain. That's a deliberate design choice and if you live in that style it pays for itself. I don't. I write small functions that return discriminated unions and narrow them with `if` and [`ts-pattern`](https://github.com/gvergnaud/ts-pattern), and a `Result` that doesn't match that shape is a `Result` I wrestle with. Cadence has also slowed — last release was February 2025. - **`ts-results`** — last published May 2022. Effectively dead. - **`oxide.ts`** — last published October 2022. The API is the closest to what I want, but I'm not betting new code on a library that hasn't shipped in over three years. - **`effect`** — actively maintained, well thought out, the most ambitious of the bunch. The reason I didn't pick it is that it isn't a `Result` helper, it's a whole programming model. Adopting `effect` means adopting fibers, the `Effect` type, a runtime, and a small ecosystem on top. It's also backed by [Effectful Technologies Inc.](https://www.effectful.co/), which is fine, but it does mean you're writing code in a paradigm that lives or dies with one company. For the kind of JS I write that's a bigger commitment than the work warrants. If you're starting a new service and want to live there, it's genuinely impressive. None of these are bad. They just don't match the way I want this primitive to feel. The other reason is that `Result` was never going to live alone. `massaman` is a full functional toolkit picking up where ramda left off, with `Option`, `attempt` / `attemptAsync` for wrapping throwing code at the boundary, `pipe` for chaining, and the same `Result` primitives all in one place. Each piece is one file, one test suite, and a chance to make a decision about what "good API" means in TS for the kind of code I actually write. ## 🎉 the conclusion The TypeScript I was writing in 2022 had exceptions in it, freely thrown, freely caught, freely forgotten. The TypeScript I'm writing in 2026 has `Result` at every boundary that can fail, and `try/catch` only at the very edge where I'm wrapping someone else's code that doesn't know better. That shift came from Rust, and I don't see it going back. Part three is on `Option` and what it does to my `null` checks. Part four is probably on `?` and what it would take to get even a watered-down version in TypeScript. I'll keep posting these as the library lands. If you want to argue with any of this, or yell at me about reinventing the wheel, reach out on [X](https://twitter.com/zrosenbauer) or [me@zrosenbauer.com](mailto:me@zrosenbauer.com). --- # how rust ruined javascript: match source: https://zrosenbauer.com/gui/blog/posts/rust-ruined-javascript-match _2026-05-12 · 3 min · @zrosenbauer · tags: typescript, rust, dx_ I've been writing [Rust](https://www.rust-lang.org/) on the side for a little over two years now. Year one was mostly dabbling, a side language I'd pick up on weekends and put down the second a Joggr deadline hit. The last six months are where I've actually leaned in. The "this language will change how you think" promise almost always fails to deliver. Rust did, almost immediately. The first place it hit was my reflex for branching on the shape of a value. `match` ruined `switch` for me, ruined the long `if/else` chains I'd been writing my entire career, and I haven't been able to write either in TypeScript since without flinching. This is part one of an opinionated series I'm calling "how rust ruined javascript", a running set of patterns I picked up from Rust that I now miss every day in TypeScript. Next post is on (cough cough `Result`). ## 📝 the background For years my TypeScript reflex for "branch on the shape of this thing" landed in one of three places, all of them mediocre: ```ts switch (event.kind) { case 'click': return handleClick(event); case 'hover': return handleHover(event); case 'submit': return handleSubmit(event); default: { const _exhaustive: never = event; throw new Error(`unhandled event: ${_exhaustive}`); } } ``` That's the "good" version. Discriminated union, exhaustiveness asserted via `never`, technically correct. It also costs three lines of boilerplate at the bottom of every `switch` just to make the compiler shout at me when I add a variant. I can't destructure inside the case, I can't match on the shape of `event.payload`, and if I forget the `default` block TypeScript happily ships a `switch` that silently falls through on whichever variant I added last Tuesday. The other two options were `if/else` ladders (worse) and a record of handlers keyed by tag (clever, but you lose narrowing on the payload the moment you index in). ## 🤯 the discovery `match` got me on first contact. The first time I used it on a real enum I knew it was going to replace every `switch` I'd ever write, and I was hunting for the TypeScript equivalent before the week was out. ```rust enum Event { Click { x: i32, y: i32 }, Hover { target: String }, Submit(FormData), } fn handle(event: Event) -> Response { match event { Event::Click { x, y } if x < 0 || y < 0 => Response::ignore(), Event::Click { x, y } => Response::click(x, y), Event::Hover { target } => Response::hover(target), Event::Submit(data) => Response::submit(data), } } ``` A few things hit me at once: 1. The whole block is an expression. It evaluates to a value, so I can `return` it directly or hand it to a `let`. 2. It's exhaustive by default. Drop a variant and the compiler stops the build, with no `never` workaround needed on my end. 3. The pattern destructures for you. `Event::Click { x, y }` gives you the fields right there in the arm, no extra line below to pull them out. 4. Guards live inside the arm. `if x < 0 || y < 0` sits on the pattern itself instead of being shoved into a nested `if` in the body. It took me longer than I'd like to admit to fully register what I was looking at. The thing I had been constructing in TypeScript out of `switch` plus `never` plus a layer of personal discipline is a first-class feature in Rust. Not an idiom, not a pattern, not a workaround dressed up in a tutorial. Actual syntax in the language, with the compiler enforcing it. > [!TIP] > Exhaustiveness is the feature. The syntax is the bonus. ## 🔧 enter ts-pattern The day after that Rust function I went back to a TypeScript file, started writing a `switch`, and stopped halfway through. The TypeScript ecosystem is too large and too obsessive to leave a gap this size unfilled. Someone had to have built this. Someone had. It's called [ts-pattern](https://github.com/gvergnaud/ts-pattern), and it's the closest thing to Rust's `match` you can get without leaving the type system. Same handler, rewritten: ```ts import { match, P } from 'ts-pattern'; type Event = | { kind: 'click'; x: number; y: number } | { kind: 'hover'; target: string } | { kind: 'submit'; data: FormData }; function handle(event: Event): Response { return match(event) .with({ kind: 'click', x: P.number.negative() }, () => Response.ignore()) .with({ kind: 'click', y: P.number.negative() }, () => Response.ignore()) .with({ kind: 'click' }, ({ x, y }) => Response.click(x, y)) .with({ kind: 'hover' }, ({ target }) => Response.hover(target)) .with({ kind: 'submit' }, ({ data }) => Response.submit(data)) .exhaustive(); } ``` `.exhaustive()` is the line that pays for the library on its own. If I add a new variant to `Event` and forget to handle it, the TypeScript compiler refuses to build. That single method call replaces the runtime `throw`, the `_exhaustive: never` boilerplate, and the `default` arm I'd inevitably forget to write anyway. Bonus: the pattern itself does the narrowing, so `({ x, y })` is fully typed inside the arm without a cast. It also does the stuff `switch` could never: - Match nested shapes (`{ kind: 'submit', data: { valid: true } }`). - Match on predicates via `P` (`P.string`, `P.number.between(0, 10)`, `P.array(P.string)`). - Bind values out of the pattern with `P.select()`. - Return a value (it's an expression, like Rust). It's not as elegant as `match` in Rust, but in fairness no userland TypeScript library can be. The method-chain shape is the price of doing this in userland instead of inventing syntax. The semantics are almost identical though, and after a week of writing it that way I stopped registering the chain at all. ## 🤷 what about native? There is a [TC39 pattern matching proposal](https://github.com/tc39/proposal-pattern-matching). It would give us something like this, natively in JavaScript: ```js const result = match (event) { when ({ kind: 'click', x, y }) when (x < 0 || y < 0): Response.ignore(); when ({ kind: 'click', x, y }): Response.click(x, y); when ({ kind: 'hover', target }): Response.hover(target); when ({ kind: 'submit', data }): Response.submit(data); }; ``` That syntax is gorgeous. It's also been at Stage 1 since May 2018, which is approaching eight years without advancing a single stage. Stage 2, Stage 3, then engine work across V8, JSC, and SpiderMonkey. Realistic shipping window is 2027-2028 at the earliest. Probably later. I'm not waiting. ## 🎉 the conclusion `ts-pattern` has been in my workflow since roughly the week I found `match`, and it's lived in my starter template for going on a year and a half. Every new file with more than one conditional branch on a shape gets `match().with(...).exhaustive()` instead of a `switch`, and existing `switch` blocks get converted opportunistically whenever I'm in the file for another reason. The cost is one dependency and a method-chain shape that takes a few hours to stop noticing. What I get back is exhaustiveness the compiler enforces and destructuring that just works. The match-as-expression ergonomics I missed every time I came back from a Rust session are the part I didn't know I needed. Up next: how `Result` and `Option` ruined exceptions for me, and what I'm reaching for in TypeScript to get the same feel. If you have any questions or want to argue with me about any of this, reach out on [X](https://twitter.com/zrosenbauer) or [me@zrosenbauer.com](mailto:me@zrosenbauer.com). --- # Scoping react-dnd source: https://zrosenbauer.com/gui/blog/posts/react-dnd-scoping _2024-06-03 · 3 min · @zrosenbauer · tags: react, gotchas_ At [Joggr](https://joggr.ai) we use [react-dnd](https://react-dnd.github.io/react-dnd/) for drag-n-drop functionality. It's a great library, but it has one major downside... it breaks drag-n-drop everywhere else on the page. TL:DR? If you want to skip the story and just see the fix, [click here](#-the-fix). ## 📝 the background If you haven't heard of Joggr (I'm the CTO & Co-Founder), we're the developer toolkit for building with AI agents. (At the time of this post we were focused on docs in your IDE — the product has evolved a lot since.) We use [TipTap](https://tiptap.dev/) which is built on top of [ProseMirror](https://prosemirror.net/) for our custom-built editor. TipTap/ProseMirror has a great drag-n-drop API that we use to drag blocks around in our editor and it was one the first features we added when we launched to our design partners last year. We recently added the ability to create folders and organize your JoggrDocs, including the ability to drag and drop JoggrDocs and folders around in the sidebar (see below). ![JoggrDocs Sidebar](/img/blog/posts/react-dnd-scoping/joggr-dnd.gif) We didn't know it at the time but releasing this new feature was the beginning of our drag-n-drop problems. ## 🐛 the bug A user logged a bug: > I can't drag-n-drop code in the editor anymore. Is this a bug or is this a feature I need to request? We already had drag-n-drop functionality in our editor so we were confused as we had made 0 changes to the editor in the last week or two. I figured it was due to the fact I had handwritten the drag-n-drop functionality in our editor and it was a bug in my code. I personally spent hours trying to re-implement the drag-n-drop functionality using the great [templates provided by TipTap](https://templates.tiptap.dev/) to no avail. I was stumped, I couldn't figure out why the drag-n-drop functionality in our editor was broken. I abandoned the fix and moved on to higher priority tasks, as this was _only 1 user_ reporting the issue (for now...). ## 🔎 the hunt After another 2-3 users reported the same issue, I knew it was time to dig in and figure out what was going on. I assigned the task to our new engineer, [Borisa](https://github.com/borisa99), to figure out what was going on. He banged his head against the wall trying to implement the drag-n-drop functionality in our editor using the TipTap templates, just like I did. He was also stumped. He started digging in and searching things like: > ProseMirror drag-n-drop not working or > TipTap drag-n-drop not working or > drag-n-drop broken or the winner > react dnd not working ## 🤯 the discovery Luckily Borisa, figured out that `react-dnd` was the culprit through some clever search queries (using `dnd` was the key). Borisa found an [GitHub issue](https://github.com/ueberdosis/tiptap/issues/4844) on the TipTap repository that was similar to our issue and pointed to a [source issue on the `react-dnd` repository](https://github.com/react-dnd/react-dnd-html5-backend/issues/7#issuecomment-262267786). > [!TIP] > Search the GitHub repository issues in your OSS if Google is coming up short. `react-dnd` was overriding the drag-n-drop APIs in the browser and in turn breaking drag-n-drop everywhere else on the page. We realized that this is not only impacting dragging blocks in our editor, but also dragging files into our image uploader. ## 🔧 the fix After digging into the issues we found that the fix was simple, we just needed to scope `react-dnd` to a specific area of the page. This is how we did it. We already had our `DndProvider` in a scoped section of our app but we didn't properly scope the `DndProvider`: ```tsx export const DirectoryTreeView: React.FC = (props) => { return ( ); }; ``` The fix was super simple, we just needed to wrap our `DndProvider` in a simple HTMLElement that we could scope to using refs: ```tsx /** * Provider with custom options (scoped root element) for the DndProvider. */ export const DirectoryTreeView: React.FC = (props) => { const [dndArea, setDndArea] = React.useState(null); const handleSidebarRef: React.RefCallback = React.useCallback((node) => { setDndArea(node); }, []); const html5Options = React.useMemo(() => ({ rootElement: dndArea }), [dndArea]); return ( {!_.isNil(dndArea) && ( )} ); }; ``` This fixed our issue and we were able to drag-n-drop blocks in our editor and files into our image uploader again. ## 🎉 the conclusion I (& Borisa) hope this helps you if you are running into the same issue. If you have any questions feel free to reach out to me on [X](https://twitter.com/zrosenbauer) or [me@zrosenbauer.com](mailto:me@zrosenbauer.com). --- # hello world source: https://zrosenbauer.com/gui/blog/posts/hello-world _2024-05-10 · 3 min · @zrosenbauer · tags: fun_ The proverbial first program that every programmer writes. ```javascript console.log('Hello World'); ``` Well I probably used VBScript or something like that, but you get the idea. ## 🤨 Who am I? My name is Zac, and I've spent over a decade working in startups as a software engineer, manager, and executive. My day-to-day stack lives in three places, but I'm a purveyor of all languages and reach for whatever fits the job: - **TypeScript & Node.js** - I started on Node back in 2015 and have been shipping APIs, services, and developer tools on it ever since. TypeScript came along and never left. - **Rust** - my current obsession. Anywhere I need real performance, predictable memory, or rock-solid CLIs — Rust is where I've been spending a lot of time lately. - **React + DevOps along the way** - I had the pleasure of using `createReactClass` back in the day, and being the engineering lead in startups meant DevOps usually fell to me too. Both have stuck around as second-nature tools. I've also had the opporunity to do some neat things such as: - **Went through Techstars** in NYC, where I was able to learn from the best, such as David Cohen, a Co-founder at Digital Ocean and a Co-founder at GitHub. - **Built a fraud detection system** at Precognitive.io that was used by Banks and E-Commerce companies. - **Built & passed multiple SOC 2 & PCI certifications** at Precognitive.io, ShopRunner, and FedEx Dataworks. - **Ran a large organization** at FedEx Dataworks, where I ran an organization of 100+ engineers, with 8 managers. ## 🤷 Who cares? Why am I telling you all this... because over the years I've been fortunate to work with some amazing people and build some great products. I've also made a lot of mistakes along the way. My hope is that through my blog I can share some of the things I've learned and help others avoid some of the mistakes I've made, or at least make new ones. 😅 --- # kidd source: https://zrosenbauer.com/gui/projects/kidd _repo: github.com/joggrdocs/kidd · status: active_ A small, opinionated toolkit out of Joggr. Built to make a recurring engineering task disappear into a single command. --- # lauf source: https://zrosenbauer.com/gui/projects/lauf _repo: github.com/zrosenbauer/lauf · status: active_ Discover, validate, and execute TypeScript scripts with Zod-powered arguments. --- # massaman source: https://zrosenbauer.com/gui/projects/massaman _repo: github.com/zrosenbauer/massaman · status: active_ My "perfect-ish" successor to ramda (now effectively unmaintained) and es-toolkit, after years of writing functional JS and wanting the missing primitives to live alongside the rest of my helpers. --- # viteval source: https://zrosenbauer.com/gui/projects/viteval _repo: github.com/viteval/viteval · status: active_ Vite-native eval framework for AI applications — write, run, and CI your LLM evals like tests, with the dev loop you already use. --- # voltagent source: https://zrosenbauer.com/gui/projects/voltagent _repo: github.com/voltagent/voltagent · status: active_ Open-source TypeScript framework for building, observing, and shipping AI agents. Core contributor — focused on agent runtime, tooling, and developer ergonomics. --- # zpress source: https://zrosenbauer.com/gui/projects/zpress _repo: github.com/joggrdocs/zpress · status: active_ A Joggr utility for compressing and packaging engineering knowledge into something an LLM can actually consume. --- # zrosenbauer.com source: https://zrosenbauer.com/gui/projects/zrosenbauer _repo: github.com/zrosenbauer/zrosenbauer.com · status: active_ My personal site — Next.js + contentlayer, deployed to GitHub Pages. Two front-ends share the same content: a graphical UI and a TUI you can drive from the keyboard. --- # Coding States source: https://zrosenbauer.com/gui/designs/coding-states _A set of images that represent different states of coding. · mode: dark_ # Background I initially created these as banners for Spotify Playlists I created for coding, such as my "Angry" or "Focused" playlists. I unintentionally created a set of images that represent different "states of coding", or at least how I feel when I'm coding. As you can see in the images I'm wearing my favorite baseball cap, NYCFC (New York City Football Club), and my **I'm-too-lazy-to-pick-out-an-outfit-everyday** black t-shirt (I have a drawer full of them). # The States Below are the different states of coding, with a brief description of each (yes... I'm going to talk about "Zac" in the third person). ## angry "Angry Zac" smashes the keyboard while listening to Death Metal. ## focused "Focused Zac" put his headphones on but forgot to play music, because he's so focused (cough cough **ADHD**). ## energized "Energized Zac" decided to drink a couple energy drinks (probably [Club Mates](https://en.wikipedia.org/wiki/Club-Mate)). ## techno "Techno Zac" is coding while listening to deadmau5 (probably has "while 1\<2" on repeat) music or something from Berlin. ## bierzeit "Bierzeit Zac" is attempting to hit the [Ballmer Peak](https://xkcd.com/323/). ## broke prod "Uh-oh, Broke Prod Zac" is trying to figure out the age-old-question: "But it worked on my machine..." --- # Editors & Claude source: https://zrosenbauer.com/gui/designs/editors-and-claude _Mock IDE windows and Claude Code sessions I drop into READMEs when a real screenshot would be wrong or distracting. · mode: dark_ # Background I kept reaching for a generic "screenshot of a code editor" or "screenshot of an agentic CLI" when writing READMEs and design docs, and I never found one that wasn't either too branded or too ugly. So I drew my own. They live in [zrosenbauer/art](https://github.com/zrosenbauer/art) alongside the rest of the collection. # The Mocks 660×420, macOS chrome, light-on-dark. The "abstract" variants swap real text for tinted bars, which is what I use when the language or content shouldn't matter to the point I'm making. ## code editor Sidebar tree, tab bar, line-number gutter, and a 20-line TypeScript React Button open in the editor. Reach for it when the post is actually about TypeScript or React. ## code editor (abstract) Same chrome, source swapped for tinted bars. Language-agnostic, for posts about flow instead of syntax. ## code editor (empty) Sidebar tree, blank editor pane. A canvas for overlays or annotations on top. ## code editor (abstract, empty) Bars in the sidebar, nothing in the editor. The most generic IDE silhouette this site will ever ship. ## claude code Welcome banner, a user prompt, two tool calls (Read, Write), an assistant response, the input box, and the status line. A full Claude Code session rendered as art, for posts that name the tool by name. ## claude code (abstract) Same session, every line rendered as tinted bars. For when "agentic CLI" is the right level of abstraction and pinning it on Claude Code specifically would be misleading. --- # Skillicons source: https://zrosenbauer.com/gui/designs/skillicons _A small set of 256×256 squircle icons for tools I actually use, drawn flat-single-color so they sit next to each other on a badge row without fighting. · mode: dark_ # Background The existing skill-icon sets I could find on GitHub don't include the tools I reach for daily. So I made my own with a single house style: a flat single-color glyph centered on a brand-color squircle, 256×256. Sources live in [zrosenbauer/art](https://github.com/zrosenbauer/art). # The Icons ## claude code Radial spark on a clay squircle. For the agentic CLI Zac is somehow always writing about. ## fastify The framework that quietly runs most of Zac's APIs. ## opencode Pixel-blocky monogram on an orange squircle. Vibes. ## pulumi Sun-burst on a purple squircle. Infrastructure as actual code, the way Zac prefers it over a wall of YAML. ## tanstack start Concentric rings on a red squircle. The "what if Next.js was actually a router again" stack. ## vercel ai sdk Triangle with an AI dot on a black squircle. Zac's go-to when the LLM call lives in product code, not in a script. ## voltagent Bolt glyph on an emerald disc set in a dark squircle. The TypeScript agent framework, contributing on the runtime and tooling side. ## wezterm Terminal mark on an indigo squircle. The terminal Zac has logged the most hours in.