← Changelog

flave is a desktop app now, and the theme is a file you can open

It opens a folder. You see your files. You click themes/lossless.css, change a value, and the document you are reading restyles under your hands — then it is still there tomorrow, because it lives in the workspace and not inside any one document. Tauri on NixOS, in about an hour.

Why Care?

Yesterday flave styled its document with a theme.css nobody could see or reach. It was baked into the app — invisible to the person whose brand it was supposed to be carrying, and gone the moment you started a second document.

That is backwards. The theme is the part a person most wants to control, and the part most likely to already exist somewhere: in their website’s stylesheet, in last quarter’s deck, in a brand kit someone made once.

Now flave opens a folder. The files are right there in a rail. Click themes/lossless.css, change a value, and the document restyles as you type. Start a new document tomorrow and the theme is still there.

What’s New?

  • A real desktop app. Tauri v2, running on Linux/Wayland.
  • A Files rail listing the workspace, toggling with Chat so the document stays the widest thing on screen.
  • Open anything — .md, .css, .yaml, .json — with the right language mode, in the same Source pane.
  • Live theme editing. Edits apply with no reload and save to disk on a debounce.
  • A seeded workspace with themes/lossless.css already in it, lifted from flave’s own Press Room identity.

Two decisions the owner made, and one scope cut that had to be un-cut honestly

D-26 — bring Tauri forward. “Tauri wrapper should be obvious and easy, and I wanna see it on Linux, let’s do that first.”

My recommendation had been a dev-server bridge first, with Tauri third. The owner’s call is better, and it is worth naming why: I was optimising for §1.1’s ordering, and they were optimising for seeing the actual product on their actual OS. Bringing the native host forward does not work around the filesystem problem, it deletes it — no bridge to build, no Chromium-only gesture gate, real files on day one.

But §1.1 says, in as many words, “Deliberately absent: Tauri (a local web app suffices; native file access comes later).” §1.1 is the scope contract. A cut that quietly reverses is how a scope discipline dies, so the reversal is annotated in place, at the cut, with the reasoning. Everything else in that list stays cut.

D-27 — the left rail toggles Chat ⇄ Files. One rail, two surfaces, never both. Four columns competing for width would make this an IDE, and the whole claim is that you are reading a document.

The NixOS part, which is usually the hard part

“Toolchain trouble generates more loops than logic ever does” is the spec’s line, and a Tauri build on NixOS is exactly where it bites. So viability got proven before anything was planned around it:

$ nix develop --command …
cargo 1.97.0
  webkit2gtk-4.1     2.52.5
  gtk+-3.0           3.24.52
  libsoup-3.0        3.6.6
  PKG_CONFIG_PATH entries: 52

The trap: nix shell nixpkgs#webkitgtk_4_1 puts binaries on PATH and leaves the .pc files behind, so pkg-config reports every dependency MISSING and Tauri will not configure. A mkShell with proper buildInputs wires PKG_CONFIG_PATH correctly — which is the entire reason flake.nix exists in this repo rather than a shell incantation in a README.

First cargo build: 1 minute 4 seconds. The app launched on the first try.

The filesystem, deliberately not a plugin

Tauri ships tauri-plugin-fs. We did not use it. The plugin’s capability scoping is a second permission model to keep in sync with the first, and what the app actually needs is a narrow contract rooted at one folder:

#[tauri::command] fn fs_tree(root: String)  -> Result<Vec<FsNode>, String>
#[tauri::command] fn fs_read(root, path)    -> Result<String, String>
#[tauri::command] fn fs_write(root, path, contents)

Every path is resolved against the root and rejected if it escapes — absolute paths and any .. component are refused before touching disk. A traversal in a document can never reach outside the folder you opened. Three commands express exactly the WorkspaceFs interface and nothing more.

That interface still has a second implementation — a MemoryFs so pnpm dev works in a plain browser. It is explicitly not persistent and says so in the status bar, rather than pretending to save and quietly losing the work.

What is honestly not done

  • No folder picker. The workspace path is resolved by the host relative to the repo. Opening an arbitrary folder is the obvious next step and is not built.
  • No CSS diagnostics in the editor yet. The Phase 2 plan wants check-styles.mjs’s unknown-token rule surfaced while you type; today it only runs at proof time.
  • No file watching. Edit a file outside the app and the tree will not notice.
  • Still no codified browser drive. Third time of saying it. The app is now a desktop binary, which makes automating a click-path harder, not easier — worth deciding whether that rung is worth its cost before the surface grows again.

What’s Next

components/ as the second persistent folder, so a trigger-pack invented once stops being re-invented per document. And a folder picker, because a workspace you cannot choose is a demo rather than a tool.