flower-core 0.1.0

Frontend-neutral structural editing model for config files, over fig.
Documentation
# flower

A structural TUI editor for config files, built on [`fig`](../fig).

Where a text editor edits characters, **flower edits the tree.** You navigate the
parsed config structurally — into mappings, along sequences, down to scalars —
and edit one *value* at a time. Every change is a path-addressed, lossless
splice through fig's editor: the bytes you didn't touch (comments, key order,
blank lines, quoting) stay byte-for-byte identical, and the document is only
ever committed in a valid state.

flower is to `fig` what `bough` is to `twig`: the structural editor over the
lossless AST. (`leaf` is the *rich-text* sibling — the right model for permissive
document formats, the wrong one for strict config grammars, where free-text
editing spends most of its keystrokes in states the parser refuses to hold.)

## Status

Early prototype. Working today:

- Open a file (format detected from extension), render its structure as an
  indented, type-colored tree.
- Navigate structurally: move between siblings, into children, out to parents;
  expand/collapse containers.
- Edit a scalar in place (typed: `true`/`42`/`3.14`/`null`/text) — committed via
  `fig::Editor::replace_value`, so the splice is lossless and validated.
- Delete a mapping entry or sequence item.
- Save (writes fig's edited source back to disk).

Deliberately not here yet — see the roadmap.

## Keys

| Key | Action |
|-----|--------|
| `j` / `k` (or ↓/↑) | next / previous row |
| `l` (or →) | expand container, or step into first child |
| `h` (or ←) | collapse container, or step out to parent |
| `Enter` / `Space` | toggle a container / edit a scalar |
| `e` | edit the selected scalar |
| `x` | delete the selected entry or item |
| `s` | save to disk |
| `q` | quit |

In edit mode: type to change the value, `Enter` to commit, `Esc` to cancel.

## Usage

```bash
cargo run -- path/to/config.toml
```

Supported formats: JSON/JSONC/JSON5, YAML, TOML, ZON, and the `fig` dialect
(`.fig`/`.figl`) — anything fig's default feature set parses.

## Architecture

A Cargo workspace, split so the editing logic is frontend-neutral (mirroring
`leaf-core` / `leaf-ratatui` / `leaf-tui`):

```
fig (Zig) → fig-sys (FFI, libfig.a) → fig crate (Editor/Document/Value)
                          crates/flower-core   (the model — no UI, no fs)
                    ┌────────────────────────┼─────────────────────────┐
      crates/flower-ratatui        crates/flower-ffi            crates/flower-tui
         (ratatui widget)        (UniFFI C-ABI binding)      (app: file I/O + loop)
                              packages/flower-swift
                          (FlowerFFI + FlowerUI, SwiftUI)
                               apps/flower-editor
                             (macOS/iOS example app)
```

### The tiers

| Tier | Path | What it is |
|------|------|------------|
| core | [`crates/flower-core`]crates/flower-core | the frontend-neutral model — the navigable `Row` tree + path-addressed lossless edits over fig. No UI, no fs. |
| widget | [`crates/flower-ratatui`]crates/flower-ratatui | a `draw(frame, &Model, header)` ratatui widget. |
| binding | [`crates/flower-ffi`]crates/flower-ffi | the **UniFFI C-ABI binding** — wraps the filesystem-free `Model` so a native Apple app can drive it. The native-Apple peer of the ratatui widget. |
| app | [`crates/flower-tui`]crates/flower-tui | the terminal app (binary `flower`) — file I/O + event loop. |
| Swift SDK | [`packages/flower-swift`]packages/flower-swift | the Swift Package — `FlowerUI`, a SwiftUI structural tree editor, over the UniFFI `flower-ffi` binding. |
| Swift app | [`apps/flower-editor`]apps/flower-editor | the cross-platform (macOS + iOS) SwiftUI example, consuming `packages/flower-swift`. |

The Swift frontend keeps the same contract as the TUI: **core owns the model**
(the tree, selection, and every lossless edit), the frontend only renders the
visible rows and forwards navigation / edit intents by row index. Every call
across the FFI returns a `DocView` — the flat visible-row list plus selection,
dirty, and status — one crossing that both mutates and repaints.

```sh
cargo run -- path/to/config.toml          # the TUI
apps/flower-editor/bootstrap.sh           # generate the Swift binding + Xcode project
```

`flower-ffi` builds today for the `aarch64-apple-darwin` slice (fig-sys ships a
prebuilt macOS-arm64 static lib); the other Apple slices (macOS-x64, iOS,
iOS-sim) build fig from source via Zig cross-compiling and are the next step for
a distributable `FlowerFFI.xcframework`.

- **`crates/flower-core`** — the frontend-neutral model. Depends only on `fig`
  and `std`.
  - `format.rs` — file extension → `fig::Format`.
  - `tree.rs` — flattens a `fig::Value` into navigable `Row`s, each carrying its
    `fig` path (a `Vec<Seg>` of `Key`/`Index`) — exactly what `fig::Editor` ops
    take — honoring a collapsed-set.
  - `model.rs``Model`: owns the `fig::Editor` (source of truth), the derived
    `Value`/rows, selection, and the edit ops. Constructed from bytes; the
    embedder owns the file.
- **`crates/flower-ratatui`** — a `draw(frame, &Model, header)` widget. Depends
  on `flower-core` + `ratatui`.
- **`crates/flower-tui`** — the terminal app (binary `flower`): reads the file,
  runs the event loop, writes on save. Depends on both.

The read path is `fig::Document::to_value()` (a semantic `Value` tree); the write
path is `fig::Editor`'s path-addressed ops. After every edit the model re-derives
the tree from `Editor::source()`, so the editor's owned source is always
canonical.

### The commit-sink `Backend` trait

`flower-core::Model` is generic over a `Backend` — it never touches a concrete
editor. It builds path-addressed `EditOp`s, applies them through the backend,
and reads the tree back via `Backend::to_value`:

```rust
pub trait Backend {
    fn apply(&mut self, op: EditOp) -> Result<(), BackendError>;
    fn to_value(&self) -> Result<Value, BackendError>;   // metadata region, for rendering
    fn source(&self) -> Result<String, BackendError>;    // full bytes, for save
}
```

- `FigBackend` (in flower-core) drives a raw `fig::Editor` — a standalone config
  file.
- A **prov backend** (`ProvBackend`, in the [`provui`]../provui repo) drives
  the *metadata region* of a prov document through prov's carrier-aware
  `MetaEditor`, leaving the prose body untouched. That composition (flower for
  metadata + leaf for the body, over one prov document) is proven by a headless
  test in provui.

flower-core stays config-generic and prov stays consumer-agnostic, so the
app-specific bridge lives in provui, not here — flower doesn't depend on prov.

## Roadmap

- **Value-editing affordances**: type-aware widgets (bool toggle, enum picker,
  number stepper) instead of one free-text field; today's edit coerces by
  literal shape, which a schema layer would fix.
- **Insert**: add keys / append sequence items (`fig` already exposes the ops).
- **Reorder / move**: `move_key`, `reorder_keys`, `move_item`.
- **Comments**: show and edit leading/trailing comments (`fig::Editor` exposes
  `leading_comment`/`set_trailing_comment`/…).
- **Schema layer**: the big one — fig has none, so a "what keys/values are valid
  here" layer is ours to add; unlocks completion, typed widgets, validation.
- **Native frontend affordances** (`FlowerUI`): today it edits scalars in one
  inline text field. Next: type-aware widgets (bool toggle, number stepper, enum
  picker), keyboard navigation, insert/reorder, and comment display — the same
  roadmap the TUI has, in SwiftUI.
- **The rest of the Apple slices**: cross-compile `fig` via Zig for macOS-x64,
  iOS, and the simulator so `scripts/build-xcframework.sh` produces a full
  `FlowerFFI.xcframework`.