Open-source project
subtleGradient/Appify-UI avatar
subtleGradient/Appify-UI

Appify UI wraps local tools in small Mac apps that use a WebKit window

Create the simplest possible Mac OS X apps. Uses HTML5 for the UI. Supports scripting with anything and everything

835 stars49 forksHTMLMIT

At a glance

What is it?
Appify UI is a set of native Mac app bundles that put an HTML5 interface around tools that already work, with one shared Swift host that opens a document, starts the app's own server command, waits for APPIFY_HOST_OPEN_URL and shows the result in a WebKit window. It is MIT licensed, it accepts no pull requests, and its author describes it as serious software with a toy-like public posture.
Who is it for?
Adopt Appify UI as a reference, or fork it, when you want to see how a small native shell can front a tool that already runs on a machine, and you prefer a WebKit window over a web browser. Do not expect a maintained dependency, because the repository accepts no pull requests and an issue carries no support obligation.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 57 days ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A native shell around a tool that already works

The opening line of the README sets the tone: making a tiny Mac app should not feel like starting a religion. The project 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.

The description in the repository makes the same point in a list of what it is not. 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.

That last phrase is the design brief, and it explains the whole tree. If the tool you want to wrap already runs on the machine, the job of the app is to give it a document type, a window, and a menu, and to keep the ceremony small. The HTML5 interface is a means to that end rather than the point of the project, and the repository description says the apps support scripting with anything and everything.

The project is MIT licensed, its primary language is HTML, and the last push to the master branch was on 2026-08-26. There is exactly one release in the published history, lazygit-v0.1.0 for LazyGit.app, published on 2026-05-20, which is a fair signal of where the project sits.

For the reader, the thing to notice is the vocabulary. The apps are not called wrappers or clients. They are called app bundles, in the macOS sense, and the README uses the word contract, which tells you the author thinks of this as an interface specification rather than a set of utilities.

Nine app bundles, one host, and a checked-in binary

The repository is intentionally object-first, and the README shows the tree:

text
.
├── 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 the README states is that each *.app directory contains everything specific to that app: runtime payloads, app servers, runners, scripts, fixtures, docs and developer tooling. Root apps are repo-bound thin apps that delegate through a tiny launcher shim.

Two directories carry the weight. source/AppifyHost holds the shared SwiftPM document host used by the app bundles, and the README says it is the only checked-in Swift source for the host. bin/appify-host is the one checked-in prebuilt host binary, and it exists for a specific reason: so that a cloned or downloaded repository can double-click the root apps without Xcode installed.

That prebuilt binary is an unusual thing to find in a repository and it is worth understanding. It trades a reviewable source tree for a working clone. If you clone the repository on a machine without a Swift toolchain, the root apps still open, and if you want to change the host you build it from source.

The other top level entries are the shared documents the project uses for itself: appify-ui.canvas, appify-ui.tiddlywiki and appify-ui.tlcanvas, which are the same formats the apps open, plus an ideas.web directory and the examples directory with a client intake form, a hello world app, and LogScope examples.

The host contract: APPIFY_HOST_OPEN_URL

The shared host is the part of the project that transfers to your own work, and the README describes its responsibilities exactly.

source/AppifyHost 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.

That last sentence is the important design statement. The host is generic, and each app supplies its own runner command. The environment variable is the hand-off point: the app's local server starts, signals the URL through APPIFY_HOST_OPEN_URL, and the host validates that URL before putting it in the WebKit view. Validating it matters, because a URL that arrives through an environment variable is exactly the kind of value a host should not paste into a web view without checking.

The result is a very small contract for app authors. You write a runner that serves something locally and prints the URL, and the host does the window, the menu and the document plumbing. That is why the README can say the host knows nothing about the individual tools.

It also explains why the apps behave differently from one another despite sharing a host. Some of them run a terminal program over ttyd, some start a Bun web runner, and some edit the file on disk directly.

Two families of app: terminal tools and document editors

Looking across the nine apps, two patterns emerge, and the README describes each one concretely.

The first family wraps a terminal program and shows it in a WebKit window. LazyGit.app opens .lazygit marker packages, where the marker lives inside a repo folder, and the app starts ttyd, runs lazygit --path for that repo and shows it in the window. LogScope.app opens log-shaped files including .log, .out, .err, .trace, .jsonl and .ndjson, starts ttyd, runs lnav and shows the indexed log timeline. tw.app opens the tabular data files supported by Tabiew, including CSV, TSV, Parquet, JSON, JSONL, Arrow, FWF, SQLite and Excel files, and runs tw the same way. litecli.app opens .db, .sqlite and .sqlite3 files and opens the selected database through a read-only SQLite URI, which is a small detail with a large consequence for a tool that opens somebody else's database file.

The second family works on document packages and writes back. JSONCanvas.app opens .canvas files, starts a bundled Bun web runner, validates JSON Canvas nodes and edges and writes the document back as plain JSON. WebFormer.app opens .webform single-file HTML documents, serves them 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. That last detail, source-span patches, is how you edit a file in place without rewriting the parts you did not touch.

The document format apps are also deliberate. Web.app opens .web packages, which are static browser-native folders of HTML, CSS, JavaScript, assets, data and relative links, using Bun's HTML routes for live reload when possible and rendering Markdown files as a convenience, and it does not make npm or build tooling part of the .web contract. WikiDock.app opens .tiddlywiki packages, which are standard TiddlyWikiFolder structures, and it does not register as a generic .html handler, which keeps TiddlyWiki documents out of the general HTML file association.

Building, ejecting and the smoke tests

The build section is a list of scripts, and the structure is uniform across the apps.

The shared host and the root apps are built and verified with two commands:

sh
Scripts/build-host-artifact.sh
Scripts/verify-root-apps.sh

To produce a standalone distributable app from a root app, there is an eject script:

sh
Scripts/eject-app.sh WebFormer.app --output /private/tmp/WebFormer.app --sign -

Inside an app bundle, the build script is now an eject wrapper, which is how an app becomes self-contained:

sh
cd WebFormer.app/Contents/Developer
Scripts/build-app.sh

Each app then has its own verification path. The terminal apps are checked with a script that drives the menu bar through AppleScript, passing the bundle identifier and the app name:

sh
cd LogScope.app/Contents/Developer
Scripts/smoke-menus.jxa.js "$PWD/../.." com.subtlegradient.logscope LogScope

The Bun-based apps run their unit tests first, and TLCanvas additionally installs with a frozen lockfile and builds a runner bundle:

sh
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

That pattern, tests before smoke tests, is worth copying even if you never use the project. The unit tests cover the runner, and the smoke script covers the app shell, and neither substitutes for the other.

The archive tag, and a repository that is cleaned on purpose

The README explains that old apps and experiments are archived by git rather than by folders in the current tree, and points at a pre-cleanup snapshot under the archive/pre-cleanup-2026-05-21 tag.

Inside that tag are the landmarks of the project's earlier directions: legacy app bundles, an AppifyUI2026 .webapp experiment, a WebappHost experiment, Appify UI 23, and a web-components-native idea sketch. Any of them can be pulled back locally with a single checkout:

sh
git checkout archive/pre-cleanup-2026-05-21 -- path/to/thing

The reason the tree is clean is stated in the layout rules: git history carries old experiments and the main tree stays clean. For a repository of app bundles, that matters more than it would for a library, because every leftover directory is another app that appears to be supported, with its own runner and its own build script, and a reader cannot tell which ones are current.

One inconsistency is visible in the file listing, and it is worth knowing about before you rely on the package.json. The root package.json is named @subtlegradient/appify-ui at version 0.1.0, marked private, and it declares a single binary named appify-ui-webapp-smoke pointing at ./source/AppifyUI2026/Scripts/webapp-smoke-runner.js. That directory does not exist in the current tree; it belongs to the archived AppifyUI2026 experiment. So the one npm entry point the manifest declares points into the archive.

The repository also carries a SPEC directory, a SECURITY.md, an AGENTS.md, a tasks directory and an examples directory, which together suggest a project with a written internal specification rather than a collection of scripts.

The boundaries: fork it, do not wait

The last section of the README is the one to read first, and it is unusually blunt. This is serious software for the author and intentionally toy-like in public posture. It is useful, inspectable, forkable, and not a support contract.

Pull requests are no longer accepted for the repository. Issues are still welcome, with bugs, notes, screenshots, ideas and look what I made chatter described as useful signal. An issue does not imply a response, a fix, a roadmap slot, a support obligation, a merge or a security review.

Then the instruction: fork it, change anything, ship your version, brag, and do not wait for the author.

That changes how you should read everything above. There is no upstream to send a patch to, so a bug you find is a bug you fix in your fork, and the shared host is a starting point rather than a dependency you can expect to be maintained. It also means the stability of the format contracts, the .web package layout, the .webform editing model and the host protocol, is something your fork owns from the first commit.

The repository is MIT licensed, which is what makes that instruction workable: fork it, change anything, ship your version, and the licence permits all three. The licence recorded for the repository listing is not a standard one that GitHub could classify, so read LICENSE before you redistribute a fork, and the SECURITY.md file is where the reporting route for a fork of this kind will be documented.

The last push to the master branch was on 2026-08-26, and with one release in the history and no pull requests accepted, the realistic way to use this project is to treat it as a worked example of the small native shell pattern and take the parts you need.

Editorial conclusion

Adopt Appify UI as a reference, or fork it, when you want to see how a small native shell can front a tool that already runs on a machine, and you prefer a WebKit window over a web browser. Do not expect a maintained dependency, because the repository accepts no pull requests and an issue carries no support obligation. Before you build on it, read the Boundaries section of the README and the SPEC directory, run Scripts/verify-root-apps.sh to see whether the checked-in bundles work on your machine, and read source/AppifyHost to understand the contract your own app has to satisfy, which is the part that actually transfers to your own project.

Frequently asked questions

What is Appify UI?

It is a set of Mac app bundles that put an HTML5 interface around local tools that already work, with one shared Swift host doing the document handling and the WebKit window. The repository is MIT licensed and contains nine apps, from LazyGit.app to WebFormer.app.

How does the shared Appify UI host open a document?

The host in source/AppifyHost opens macOS documents, starts an app-bundled server command, waits for APPIFY_HOST_OPEN_URL, validates that URL and shows it in a native WebKit window. The README notes that the host knows nothing about the individual tools such as LazyGit, Tabiew, Bun or ttyd.

Do I need Xcode installed to build the Appify UI root apps?

No. bin/appify-host is a checked-in prebuilt host binary so a cloned or downloaded repository can double-click the root apps without Xcode. Building from source uses Scripts/build-host-artifact.sh for the host and Scripts/verify-root-apps.sh to check the checked-in apps.

Can I send a pull request to Appify UI?

No. The README says pull requests are no longer accepted for this repository, while issues are still welcome as signal, and an issue implies no response, fix, roadmap slot or support obligation. The stated path is to fork the project, change anything and ship your own version.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. subtleGradient/Appify-UI on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/subtlegradient-appify-ui.svg)](https://hysenlabs.com/projects/subtlegradient-appify-ui)