Making a tiny Mac app should not feel like starting a religion.
Appify UI is for turning local software into real Mac-shaped things: a small native shell around the tool that already works, with the operating system doing the parts the operating system is good at.
Not Electron-as-a-city. Not a generic cross-platform fantasy. A folder-shaped Mac app, a tiny host, a local runner, and as little ceremony as possible between an idea and a double-clickable thing.
The repo is intentionally object-first:
.
├── LazyGit.app
├── JSONCanvas.app
├── LogScope.app
├── TLCanvas.app
├── Web.app
├── WebFormer.app
├── WikiDock.app
├── litecli.app
├── tw.app
├── ideas.web/
├── bin/
│ ├── appify-host
│ └── appify-host.manifest.json
├── source/
│ └── AppifyHost/
├── Scripts/
│ └── verify-root-apps.sh
└── README.md
The rule is simple:
*.app/contains everything specific to that app: runtime payloads, app servers, runners, scripts, fixtures, docs, and developer tooling. Root apps are repo-bound thin apps; they delegate through a tiny launcher shim.bin/appify-hostis the one checked-in prebuilt host binary used so a cloned or downloaded repo can double-click root apps without Xcode installed.source/AppifyHost/contains the shared SwiftPM document host used by the app bundles. It is the only checked-in Swift source for the host.- Git history carries old experiments. The main tree stays clean.
LazyGit.app opens .lazygit marker packages. The marker lives
inside a repo folder; the app starts ttyd, runs lazygit --path for that repo,
and shows it in a native WebKit window.
JSONCanvas.app opens .canvas files. It starts a bundled
Bun web runner, validates JSON Canvas nodes and edges, and writes the document
back as plain JSON.
LogScope.app opens log-shaped files including .log, .out,
.err, .trace, .jsonl, and .ndjson. It starts ttyd, runs lnav, and
shows the indexed log timeline in a native WebKit window.
TLCanvas.app opens .tlcanvas document packages. Its bundled
Runner is the canonical TLCanvas source, including the tldraw SDK app, server,
tests, schema, and lockfile.
Web.app opens .web document packages. A .web package is a
static browser-native folder: HTML, CSS, JavaScript, assets, data, and relative
links. It uses Bun's HTML routes for live reload when possible, renders
Markdown files as a convenience, and does not make npm or build tooling part of
the .web contract.
WebFormer.app opens .webform single-file HTML documents. It
serves the document through an app-local Bun runner, injects runtime save
affordances with HTMLRewriter, and writes edited native form state back into
the same HTML file with narrow source-span patches.
WikiDock.app opens .tiddlywiki document packages. The
package is a standard TiddlyWikiFolder with tiddlywiki.info, tiddlers/, and
the usual optional plugins/, themes/, and languages/ folders. It does not
register as a generic .html handler.
tw.app opens tabular data files supported by Tabiew, including CSV,
TSV, Parquet, JSON, JSONL, Arrow, FWF, SQLite, and Excel files. It starts
ttyd, runs tw, and shows Tabiew in a native WebKit window.
litecli.app opens .db, .sqlite, and .sqlite3 files. It
starts ttyd, runs litecli, and opens the selected SQLite database through a
read-only SQLite URI.
source/AppifyHost is the shared host layer. It knows how to open macOS
documents, start an app-bundled server command, wait for APPIFY_HOST_OPEN_URL,
validate that URL, and show it in a native WebKit window. It does not know about
LazyGit, Tabiew, LiteCLI, TLCanvas, Web, WebFormer, Bun, ttyd, or tldraw.
Shared host and root apps:
Scripts/build-host-artifact.sh
Scripts/verify-root-apps.shCreate a standalone distributable app from a root app:
Scripts/eject-app.sh WebFormer.app --output /private/tmp/WebFormer.app --sign -For any root app, the app-local build script is now an eject wrapper:
cd WebFormer.app/Contents/Developer
Scripts/build-app.shLazyGit:
cd LazyGit.app/Contents/Developer
Scripts/smoke-ui.shLogScope:
cd LogScope.app/Contents/Developer
Scripts/smoke-menus.jxa.js "$PWD/../.." com.subtlegradient.logscope LogScopetw:
cd tw.app/Contents/Developer
Scripts/smoke-menus.jxa.js "$PWD/../.." com.subtlegradient.tw twlitecli:
cd litecli.app/Contents/Developer
Scripts/smoke-menus.jxa.js "$PWD/../.." com.subtlegradient.litecli litecliJSONCanvas:
cd JSONCanvas.app/Contents/Resources/Runner
bun test tests/*.test.ts
cd ../../Developer
Scripts/smoke-ui.shTLCanvas:
cd TLCanvas.app/Contents/Resources/Runner
bun install --frozen-lockfile
bun test tests/*.test.ts
bun build src/index.html --outdir /private/tmp/tlcanvas-runner-build
cd ../../Developer
Scripts/smoke-ui.shWeb:
cd Web.app/Contents/Resources/Runner
bun test tests/*.test.tsWebFormer:
cd WebFormer.app/Contents/Resources/Runner
bun test tests/*.test.ts
cd ../../Developer
Scripts/build-app.shWikiDock:
cd WikiDock.app/Contents/Developer
Scripts/build-app.shVerify the checked-in root apps:
Scripts/verify-root-apps.shThe old apps and experiments are archived by git, not by folders in the current tree. The pre-cleanup snapshot is:
archive/pre-cleanup-2026-05-21
Useful landmarks inside that tag:
- legacy app bundles
- AppifyUI2026
.webappexperiment - WebappHost experiment
- Appify UI 23
- web-components-native idea sketch
To pull any old artifact back locally:
git checkout archive/pre-cleanup-2026-05-21 -- path/to/thingThis is serious software for me and intentionally toy-like in public posture. It is useful, inspectable, forkable, and not a support contract.
I do not accept pull requests for this repo anymore. Issues are still welcome: bugs, notes, screenshots, ideas, and "look what I made" chatter are all useful signal. An issue does not imply a response, fix, roadmap slot, support obligation, merge, or security review.
Fork it. Change anything. Ship your version. Brag. Do not wait for me.
- https://github.com/MacGapProject/MacGap2
- https://github.com/nwjs/nw.js
- https://github.com/sveinbjornt/Platypus
Those are probably still what many people should use.
Appify UI is the stubborn smaller thing: local tools, native document packages, WebKit where it helps, SwiftUI where it belongs, and as little machinery as possible between an idea and a double-clickable Mac app.