Portfolio · 2000 – present

Code & Projects

I’m a Staff Frontend Engineer at InterNations, and I’ve been building things for the web since 2000. This page collects the work I’d actually want to talk about: a travel planning product that I run on my own, a couple of internal developer tools, some small command-line tools, and a handful of open-source libraries. A few of those libraries are old enough that I’ve now rewritten them for three different eras of JavaScript. There’s a section on writing too, because I think that’s a major part of the job.

50+ repositories~700 starsgithub.com/patik

Dovetail Travel Planner

dovetail.travel

Dovetail is a trip planning calendar for people who like to work out their own logistics. You lay a trip out day by day, drop the ideas you haven’t placed yet into a wishlist until you know where they fit, see the whole thing on a map, and print an itinerary that is actually readable on paper. It runs on the web and as native iOS and Android apps, and it takes subscriptions through both Stripe and the app stores.

I do all of it myself: the design, the code, the support email, and the app store paperwork. The source is closed, so here is what I think is interesting about how it’s built.

  • Offline first. Trips live in an IndexedDB cache with a sync queue behind it. You can plan a whole day on a plane, and the app reconciles with Supabase once the connection comes back.
  • One codebase, three platforms. React 19 on Vite, wrapped in thin Capacitor shells that hand over native auth, networking, and geolocation.
  • The end-to-end tests don’t need a backend. The Playwright suite runs against seeded IndexedDB fixtures and a fake signed-in user, so CI never needs real credentials.
  • One color system for everything. Design tokens keep light and dark mode looking the same on the web and in both native apps.

Role

Everything, on my own

Stack

React 19TypeScriptViteTanStack QueryTailwind CSS 4SupabaseIndexedDBMapbox GLCapacitorPlaywright

Links

Internal developer tools

Two tools I built for InterNations. Both of them started as something I wanted for myself, and then it turned out other people wanted it too. The source is private. The screenshots and descriptions here are shared with permission.

Inc

InterNations has a Symfony console with a few hundred development and test-data commands in it. They are genuinely useful. But finding the one you want means grepping a list, reading its --help output, and guessing at the argument order. Inc reads each command’s live definition instead and builds a form out of its arguments and options. You search for the command, fill in the fields, and see the exact string that is about to run before it runs.

I wrote it in TypeScript on Bun, with React and OpenTUI for the interface and Zustand holding the state. It keeps a searchable log of what you have run, remembers recent commands, switches environments, and installs its own shell completion. If you already know the command you want, it passes straight through and doesn’t show you a form at all. It ships as a self-updating binary through GitHub Releases.

TypeScriptBunReactOpenTUIZustandCommanderVitest
$ gin
Gin showing the next and upcoming deployments, commit and pull-request counts, past releases, and Git shortcuts

Gin

InterNations ships from release branches named after dates, like deploy/2026-01-22. That is fine until you need to answer the ordinary questions. Which branch is next? Have I fetched it yet? What is in it, which of those commits are mine, and do I have any pull requests pointing at it? Every answer is a separate command, and the branch name changes twice a week. Gin puts all of it in one terminal view.

I wrote Gin in TypeScript on Bun, with React and Ink for the interface and Zustand holding the state. It works out the right branch names from the release schedule, fetches and compares them, and asks the GitHub CLI about your pull requests. It also works as a transparent Git wrapper. Any command it doesn’t recognize goes through to Git untouched, and keywords like deploy and upcoming expand wherever a branch name is valid, including inside ranges and refspecs.

TypeScriptBunReactInkZustanddate-fnsVitest

Command-line tools

Small tools that take some of the annoyance out of the terminal. I wrote each of these because I kept wishing it existed, which is not quite the same thing as claiming anyone else needs it.

$ git prune-branches

git-prune-branches

This answers the question of which local branches are safe to delete. It fetches and prunes first, then sorts whatever is left into groups:

  • merged, and safe to delete — these come pre-selected
  • unmerged, so deleting them needs -D — these are deliberately not pre-selected
  • everything else, listed so you can see it

Then it shows you the exact git commands before it runs any of them.

inquirer-form-prompt

inquirer-form-prompt

A form in the terminal. Text inputs, radio buttons, and checkboxes all on one screen, with keyboard navigation that works the way you would expect it to. It’s for the kind of CLI that would otherwise walk you through a dozen questions one at a time.

inquirer-grouped-checkbox

inquirer-grouped-checkbox

A checkbox prompt for lists that are too long to scroll through comfortably. Choices sit under group headers that are themselves selectable, the list filters as you type, and each group shows how many of its items you have picked so far.

inquirer-select-with-state

inquirer-select-with-state

Inquirer’s select prompt with one thing added: a banner above the choices that you can keep updating while the prompt is open. Start some async work, such as measuring a directory or calling an API, and report on it while the user is still making up their mind.

Apps & sites

Depth-of-Field Calculator

A depth-of-field calculator for photographers. You line up as many camera and lens combinations as you like, adjust the apertures and the distances, and compare the results on a chart. The entire setup is encoded in the URL, so you can send someone a link to exactly what you are looking at.

  • patik.com

    Astro with React islands, travel galleries served out of Cloudinary, and a CI pipeline that runs visual regression and accessibility tests in Playwright. The whole codebase is public, including the parts of it I am not especially proud of.

Libraries

The interesting part of these is how long they have been around. They have been rewritten to survive a change in how JavaScript gets written and shipped: <script> tags, then jQuery, then AMD modules, and now typed ES modules.

  • within-viewport

    Tells you whether an element is entirely inside the viewport, or inside any scrollable container, with a threshold you can configure for each side. It started out as a jQuery plugin in 2011 and it is now a dependency-free TypeScript package. I have kept the API steady through every rewrite, because people are still running the version they installed years ago.

  • kindjs

    typeof, but precise: kind([1, 2, 3]) is 'array', kind(null) is 'null', and kind(new Date()) is 'date'. It gives useful names to JavaScript values, DOM nodes included.

  • simple-stdout

    Run a shell command and get its stdout back as a string. It is a very small promise-based wrapper around Node’s child_process, with an execFilevariant for when the input isn’t yours to trust.

Writing

A pull request is usually the one moment when everybody who will have to live with a change is actually looking at it. That seems worth writing well, so I put real effort into these.

  • Pull request descriptions

    A selection of pull requests from a long run of frontend platform migrations. They are here for the writing rather than for the code. Each one explains what changed, why it changed, how to review it, and what the team would need to do differently afterward.

Contributions

Work from my years in the public sector, built with the New York State Office of Information Technology Services.

  • Core UI & the Excelsior Web Framework

    I was a core contributor to Excelsior, the framework behind dozens of New York State sites, and to Core UI, the component-based successor we built for web apps.

  • DMV Registration Renewal

    A responsive redesign of registration renewal, which is one of the busiest transactions the state runs.

  • Talks & code sprints

    Go Responsive came out of a code sprint on responsive web design. I also gave an Ignite talk at NYS DevCon in 2013, which is the format where your slides advance every fifteen seconds whether you are ready or not.

From an earlier era

These are still online because old links shouldn’t rot. They’re mostly interesting now as a record of what the web used to make you do.

  • console.log-wrapper

    This made console.log safe to call across the browsers of the early 2010s, including the ones where the console only existed while the developer tools happened to be open. It is completely obsolete now, which is the right outcome. The platform caught up.

  • User scripts & extensions

    Greasemonkey scripts and small browser extensions, from the era when fixing a website yourself was a normal thing to do.

There is more at github.com/patik , including the experiments and the abandoned ideas, and there is occasional writing on the blog.