LifeLab · design charter
The web build's identity was never “monospace and org syntax.” Those were symptoms of one commitment: the document is the interface — structure, data, and program all made of the same stuff, editable in place. CodeMirror made that free, so the look and the rule were the same thing and you never had to state the rule.
Native gave you real windows, real menus, real tabs, real system pickers. Those are chrome — a second substance the web build didn't have. Two substances with no stated boundary is exactly what “lost” feels like: every decision becomes a coin flip, because there's no principle to lose the argument to. The fix isn't more mono. It's writing the rule down, then letting it decide the coin flips for you.
LifeLab is a personal wiki where every surface is a block you can edit, query, and program — and the native app makes it a first-class citizen of your Mac.Two halves on purpose. The first half is what LifeLab is (unchanged from the web build — this is your identity). The second is what this app is for (new — this is its purpose, and the only reason to build native instead of shipping a URL).
Test it against your own screenshots: tabs pass (a tab set can be a block). Nested lenses pass (a query in the document). The `{filter}` placeholder in the title passes. A native date picker passes — it edits a block's property. A sidebar file tree would fail, which is why it never felt right.
01
No privileged UI objects. Page titles, lenses, tab sessions, sidebar contents, settings — all blocks, in the same tree, with the same verbs.
→ so: `* Home` stays a block, not a title bar
02
A view is never a hardcoded screen. It's a lens you can read, edit, nest, and save — including the views the app ships with.
→ so: “day/week/month” are saved lenses, not modes
03
Hooks and Lua aren't a power-user feature, they're the mechanism the app's own features are built from. Nothing the app does should be unreachable to the user.
→ so: every menu item maps to a callable command
04
Plain text/org is the substrate; PowerSync, indexes and caches are derivatives. If the app died tomorrow the notes are still complete and readable.
→ so: no state that exists only in the UI layer
What this buys you when you're lost: a feature request now has a home or it doesn't. “Can we add a kanban view?” → commitment 02: it's a lens with a group-by, not a new screen. “Can tabs sync across devices?” → 04: sessions are blocks, so yes, automatically. “Can we add a formatting toolbar?” → 01 + 04: no, because it implies markup the text can't hold.
Right now the native app's answer to “why not just the web app?” is “it's faster and right-click works.” That's a reason to build it, not a reason to want it. Native's real gift is that your wiki stops being a place you visit and becomes something the rest of the Mac can talk to. Every item below is impossible in the web build and cheap for you now:
capture — get things in without arriving
reach — let the OS call your Lua
Pick one and ship it next. Quick-capture is the answer. It's a day of work, it's the feature people describe to friends, and it proves the purpose half of the sentence: LifeLab is where your day goes, without you going anywhere.
{filter}. No architecture, no new features.goal: the app looks decided rather than portedtabs.* Lua API + on_tab_focus. Tabs become data, which means workspaces become notes — commitment 01 applied to your own chrome, and the story that makes tabs interesting rather than merely expected.goal: the new UI feature obeys the old thesis“Would this still make sense if the whole app were a text file?”Catches drift toward chrome-only state, positional UI, and hidden magic. If the answer is no, either it's wrong, or you're missing the text form of it.
“Could the user have built this themselves with a lens and a hook?”Catches special-cased features. If yes, ship it as a lens and a hook — then it's an example, a template, and a feature at once.
You said the native app “traded the styling away for practicality.” It didn't have to be a trade, and mostly it wasn't — the buffer is intact. What you actually traded away was the explanation. Write these two questions and the four commitments into the repo's README, and the next fifty decisions stop feeling like coin flips.