BigMarc f5faa27f88 GrammaGem — open-source, on-device macOS writing assistant
Native SwiftUI menu-bar app: global hotkey → capture selected text (AX API /
clipboard fallback) → grammar (Harper) / local AI (MLX) → replace in place.
Free/paid entitlement gate + Lemon Squeezy device-cap licensing, $0 marginal
cost per user. Heavyweight engines behind protocol stubs so it builds out of the box.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 13:56:26 +02:00

48 lines
1.7 KiB
Markdown

# harper-ffi
A thin Rust static library that exposes the **Harper** grammar core
(Apache-2.0) to Swift over a C-FFI. This directory is a placeholder for the real
integration (spec §1, §2, §8).
The Swift side talks to this through the `GrammarEngine` protocol; today
[`HarperEngine.swift`](../Sources/GrammaGem/Grammar/HarperEngine.swift) ships a
small pure-Swift rule stub so the system loop + tests run with zero native deps.
## Planned shape
```
harper-ffi/
├─ Cargo.toml # crate-type = ["staticlib"]; depends on `harper-core`
├─ src/lib.rs # #[no_mangle] extern "C" fns over harper-core
└─ include/harper.h # C header consumed by Swift (module map / bridging)
```
Sketch of the FFI surface:
```rust
// src/lib.rs
#[no_mangle]
pub extern "C" fn harper_check(text: *const c_char,
out_json: *mut *mut c_char) -> usize { /* … */ }
#[no_mangle]
pub extern "C" fn harper_free(ptr: *mut c_char) { /* … */ }
```
## Wiring into SwiftPM
1. Build the static lib: `cargo build --release` → `libharper_ffi.a`.
2. Add a `systemLibrary`/binary target (or `linkerSettings`/`unsafeFlags`) in
`Package.swift` pointing at the `.a` + the header's module map.
3. Replace the rule set in `HarperEngine.check(_:)` with calls to
`harper_check`, decoding the returned span JSON into `[Suggestion]`.
> Alternative considered: bundle `harper-ls` and talk to it as a subprocess over
> LSP. The FFI route is leaner (no process, no JSON-RPC) and is preferred.
## Why Harper (not LanguageTool)
Harper is ~1/50th the memory of LanguageTool and needs no multi-GB n-gram data,
so it's the right *embeddable* core. LanguageTool stays an optional power-user
backend, never the default (spec §2).