Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 47 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,25 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/images/flo-icon-dark.png">
<img src="docs/images/flo-icon-light.png" alt="Flo app icon" width="128" height="128">
</picture>
<img src="docs/images/flo-banner.png" alt="Flo: dictation that never leaves your Mac" width="720">
</p>

<h1 align="center">Flo</h1>
<p align="center">Push-to-talk dictation for Apple Silicon Macs. Fully on-device.</p>

<p align="center">
<a href="https://flo.talix.app">flo.talix.app</a> ·
<a href="https://github.com/dev-talix/flo/releases/latest">Download</a> ·
<a href="SECURITY.md">Security</a>
</p>

Push-to-talk dictation for Apple Silicon Macs that runs entirely on your
machine. Hold a key, speak, let go, and the text is pasted into whatever app
has focus. Speech is transcribed on-device with
[WhisperKit](https://github.com/argmaxinc/WhisperKit) or Parakeet
([FluidAudio](https://github.com/FluidInference/FluidAudio)), and can
optionally be cleaned up by a local LLM. No audio leaves the Mac.
Hold a key, speak, let go. Flo pastes the text into the focused app.
[WhisperKit](https://github.com/argmaxinc/WhisperKit) or Parakeet via
[FluidAudio](https://github.com/FluidInference/FluidAudio) transcribes on-device,
with optional cleanup by a local LLM. No audio leaves your Mac.

## Requirements

- Apple Silicon Mac (M1 or later). Intel isn't supported.
- macOS 14 or later. Apple Intelligence cleanup and command mode need macOS 26.
- About 2 GB of disk for the model cache. The default Large v3 Turbo model is
about 1.5 GB.
- About 2 GB for the model cache. Default Large v3 Turbo is about 1.5 GB.
- Internet on first launch only, to download the model.

## Install
Expand All @@ -36,25 +30,18 @@ and drag Flo to Applications. Builds are signed and notarized.
On first launch, grant two permissions:

1. **Microphone**, to record while you hold the key.
2. **Accessibility**, for the global hotkey and the synthesized paste. The
menubar icon shows a warning until it's granted. No relaunch needed.

The first launch downloads the model and lets Core ML prepare it, which can
take a few minutes. The menubar shows Ready when it's done.

Flo lives in the menubar and starts at login. Its icon is a row of dots:
a still wave when it's ready, a moving wave while you talk, and dots filling
in while it transcribes. It updates itself through
[Sparkle](https://sparkle-project.org) once a day (you can turn that off in
Settings). To uninstall, quit it, delete the app, and delete
`~/Library/Application Support/LocalFlow/`.

Flo was called LocalFlow before 1.8.0 and Walkie in 1.8.0. It became Flo in
1.10.0. Existing installs update in place and keep their settings,
permissions, and data.
The data folder and log file still use the LocalFlow name. Sparkle updates
can keep the old `/Applications/Walkie.app` or `/Applications/LocalFlow.app`
path until you reinstall from the DMG.
2. **Accessibility**, for the global hotkey and synthesized paste. The menubar
warns until it's granted. No relaunch needed.

Model download and Core ML preparation can take a few minutes. The menubar then shows Ready.

Flo lives in the menubar and starts at login. Its dots form a still wave when
ready, move while you talk, and fill in while transcribing. Daily updates use
[Sparkle](https://sparkle-project.org); you can turn them off in Settings.
To uninstall, quit Flo, delete the app, and delete `~/Library/Application Support/LocalFlow/`.

Flo was LocalFlow before 1.8.0, then Walkie until 1.10.0.
[Upgrades](docs/development.md#app-naming-and-upgrades) keep settings, permissions, and data.

To check a download came from this repo's release workflow:

Expand All @@ -65,35 +52,28 @@ shasum -a 256 -c SHA256SUMS.txt # attached to each release

## Using it

Hold **Right Option**, speak, release. Press **Escape** to cancel before the
text is ready. The hotkey, speech model, microphone, and HUD theme are all in
Settings.

- **Live transcript.** While you talk, finished chunks show up in a panel above
the HUD. That text is raw. The pasted text comes after release.
- **Formatting.** Long pauses start new paragraphs, and spoken ordinals
("first... second...") become numbered lists. Commands like "new line",
"new paragraph", "bullet point", "numbered list", and "thumbs up emoji" work
too. They're always interpreted, so saying "bullet point" mid-sentence
starts a list item.
- **Vocabulary and corrections.** Add names and jargon in Settings to bias
recognition (Whisper only). Teach fixes like "talex" to "Talix", or edit a
pasted dictation, copy it, and pick **Fix Last Dictation...** to learn the
swaps.
Hold **Right Option**, speak, release. **Escape** cancels before the text is ready.
Change the hotkey, speech model, microphone, and HUD theme in Settings.

- **Live transcript.** Finished chunks appear in a panel above the HUD while you talk.
This text is raw; Flo pastes the final text after release.
- **Formatting.** Long pauses start paragraphs; "first... second..." makes
numbered lists. Say "new line", "new paragraph", "bullet point", "numbered
list", or "thumbs up emoji". Commands always apply, even mid-sentence.
- **Vocabulary and corrections.** Add names and jargon in Settings to guide Whisper.
Teach fixes like "talex" to "Talix", or edit and copy a pasted dictation,
then pick **Fix Last Dictation...** to learn the swaps.
- **Snippets.** Say a trigger phrase and it's replaced with saved text.
- **History.** Every dictation is appended to a daily Markdown file in
`~/Library/Application Support/LocalFlow/History/`. The last 5 are in the
menu for copying back if a paste goes astray.
- **Retry.** If transcription fails, the audio stays in memory so you can
retry from the menu. It's gone when you quit.
- **History.** Every dictation goes in a daily Markdown file under
`~/Library/Application Support/LocalFlow/History/`. Copy the last 5 from the menu.
- **Retry.** After a failed transcription, retry from the menu. Audio stays in memory until you quit.

### Cleanup (optional)

Cleanup strips filler words, fixes false starts, and adapts style to the app
you're in (casual in Slack, identifier-safe in editors). It's off by default
because it adds latency. It runs on Apple Intelligence when that's available,
otherwise on [Ollama](https://ollama.com) with Superwhisper's
[s1-mini](https://huggingface.co/superwhisper/s1-mini):
Cleanup strips fillers, fixes false starts, and adapts to the app: casual in
Slack, identifier-safe in editors. It's off by default because it adds latency.
It uses Apple Intelligence when available, otherwise [Ollama](https://ollama.com)
with Superwhisper's [s1-mini](https://huggingface.co/superwhisper/s1-mini):

```bash
brew install ollama
Expand All @@ -105,17 +85,15 @@ If the cleanup backend is down, Flo pastes the raw transcript.

### Command mode

Hold the command key and say what you want. With text selected, it rewrites
it ("make this shorter", "translate to Spanish"). With nothing selected, it
writes at the cursor. It uses Apple Intelligence or a local Ollama instruct
model (`gemma3:4b` by default).
Hold the command key and ask to rewrite selected text: "make this shorter" or
"translate to Spanish". With no selection, Flo writes at the cursor. It uses Apple
Intelligence or a local Ollama instruct model, `gemma3:4b` by default.

## Privacy

There's no account, server, or telemetry. The only network request Flo
makes on its own is the model download (plus the update check, if enabled).
The hotkey monitor sees modifier keys only, never characters. Optional
diagnostic recordings are off by default and stay local. See
There's no account, server, or telemetry. Flo's only network requests on its own
are model downloads and enabled update checks. The hotkey monitor sees modifier
keys only, never characters. Diagnostic recordings are off by default and stay local. See
[SECURITY.md](SECURITY.md) for details and how to report a vulnerability.

## Limitations
Expand All @@ -127,30 +105,10 @@ diagnostic recordings are off by default and stay local. See

## Development

See [docs/development.md](docs/development.md) for building, the local test
channel, diagnostics, and benchmarks. Feature notes live in
[docs/flows](docs/flows).

## Cutting a release

Release Please maintains the version PR. Merging it updates the release
manifest, and that main push automatically calls
`.github/workflows/release.yml`. The workflow tests, signs, notarizes, and
packages the app, validates the DMG, Sparkle feed, and checksums, then
publishes the tag and GitHub Release. A failed run can leave a private draft,
never a partial public release. It also fails if `FoundationModels` didn't
link, since that build would ship without command mode or on-device cleanup.

Required repository secrets:

| Secret | What it is |
|---|---|
| `MAC_CERT_P12_BASE64` | Developer ID Application certificate + key, as base64 .p12 |
| `MAC_CERT_PASSWORD` | Password for that .p12 |
| `APPLE_ID` | Apple ID for notarization |
| `APPLE_APP_SPECIFIC_PASSWORD` | App-specific password for that Apple ID |
| `APPLE_TEAM_ID` | Developer team ID |
| `SPARKLE_PRIVATE_KEY` | EdDSA key matching the public key in `Info.plist` |
See [docs/development.md](docs/development.md) for builds, the local test channel,
diagnostics, and benchmarks. Feature notes live in [docs/flows](docs/flows).

See [Releases](docs/development.md#releases) for the release flow and required secrets.

## License

Expand Down
27 changes: 27 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ test without touching the release build:

## App naming and upgrades

Flo was called LocalFlow before 1.8.0 and Walkie in 1.8.0. It became Flo in
1.10.0. Existing installs update in place and keep their settings,
permissions, and data. The data folder and log file still use the LocalFlow
name. Sparkle updates can keep the old `/Applications/Walkie.app` or
`/Applications/LocalFlow.app` path until you reinstall from the DMG.

Owner: Flo maintainers. The October 8, 2026 request renames the application
from Walkie to Flo. It follows the LocalFlow-to-Walkie rename in `ad726e1`.
The visible names are Flo and Flo Local. App bundles, DMG/ZIP filenames,
Expand Down Expand Up @@ -140,6 +146,27 @@ python3 -B Tests/StartupReadinessTests.py # no mic or model download needed

CI runs all of these except the startup readiness check.

## Releases

Release Please maintains the version PR. Merging it updates the release
manifest, and that main push automatically calls
`.github/workflows/release.yml`. The workflow tests, signs, notarizes, and
packages the app, validates the DMG, Sparkle feed, and checksums, then
publishes the tag and GitHub Release. A failed run can leave a private draft,
never a partial public release. It also fails if `FoundationModels` didn't
link, since that build would ship without command mode or on-device cleanup.

Required repository secrets:

| Secret | What it is |
|---|---|
| `MAC_CERT_P12_BASE64` | Developer ID Application certificate + key, as base64 .p12 |
| `MAC_CERT_PASSWORD` | Password for that .p12 |
| `APPLE_ID` | Apple ID for notarization |
| `APPLE_APP_SPECIFIC_PASSWORD` | App-specific password for that Apple ID |
| `APPLE_TEAM_ID` | Developer team ID |
| `SPARKLE_PRIVATE_KEY` | EdDSA key matching the public key in `Info.plist` |

## CodeQL scans

[The CodeQL workflow](../.github/workflows/codeql.yml) scans main pushes,
Expand Down
Binary file added docs/images/flo-banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading