A Finder-style file manager built in Flutter, with a built-in Ollama chat panel, a Quick-Look-style preview viewer, and a workflow editor for running custom prompt chains against your local files.
Notilus is a single-developer, local-first project. It runs on macOS, Windows, Linux, iPad, and iPhone from one Flutter codebase, and talks to a local Ollama instance — no cloud, no account, no telemetry.
Current version: 0.1.5 (see pubspec.yaml).
- Finder-style three-pane layout on desktop — sidebar, file area, Info / Chat / Workflows panel — with a fluid wide layout and a bottom-tab compact layout below 750-px width (iPhone, iPad portrait split-view, narrow desktop windows).
- Quick-Look-style preview for images, text/source code, rendered Markdown, PDF, video, and audio. Sibling files in the same folder are navigable via swipe (touch) or arrow keys + space (desktop).
- Integrated terminal (desktop) — bottom-docked PTY-backed panel
with VSCode-style tabs, toggled by ⌘J (macOS) / Ctrl+J
(Windows / Linux). New sessions inherit the current folder as their
working directory; switching folders sends
cdto the active tab. - Disk-cached thumbnails for PDFs (first page), SVGs, and text files (first-lines snippet). Cache keys include path + mtime + size, so an external edit invalidates the entry automatically.
- Live filesystem updates — the current folder is watched with
Directory.watch()and re-listed automatically on create / rename / delete (debounced ~180 ms). No manual refresh button needed. - Ollama chat panel with token-by-token streaming over
/api/generate. Attach the selected file to send its extracted text (PDF viapdftotext, Office docs via LibreOffice) or pass images straight through as base64 to a vision-capable model. - Multi-step workflows — chain prompts (each with its own template and optional model override) and run them against the selected file.
- System Overview screen — disk usage per drive, shallow folder breakdown for Desktop / Documents / Downloads, plus an "AI Insights" panel that asks your local model for cleanup suggestions.
- Native context menu on right-click / long-press with Open, Open With (default app + system chooser), Rename, Duplicate, Copy Path, Reveal in Finder / Files, and Move to Trash.
- Pure-neutral dark theme (R = G = B grays) plus a light theme; follows the system appearance by default and can be overridden in Settings.
Prebuilt binaries are published on the Releases page.
macOS / Linux:
curl -fsSL https://raw.githubusercontent.com/JayashBhandary/Notilus/main/install.sh | shWindows (PowerShell):
irm https://raw.githubusercontent.com/JayashBhandary/Notilus/main/install.ps1 | iexThe installer detects your platform, downloads the latest release asset, and installs to:
| Platform | Install location |
|---|---|
| macOS | /Applications/Notilus.app |
| Linux | /opt/notilus (symlinked at /usr/local/bin/notilus) |
| Windows | %LOCALAPPDATA%\Programs\Notilus (Start Menu shortcut added) |
Grab the asset for your platform from the Releases page:
| Asset | Target |
|---|---|
Notilus-<v>-macos-arm64.dmg |
Apple Silicon Macs (M1/M2/M3+) |
Notilus-<v>-macos-x64.dmg |
Intel Macs |
Notilus-<v>-macos-universal.dmg |
Either architecture (larger file) |
Notilus-<v>-windows-x64.zip |
Windows 10/11 x64 |
Notilus-<v>-linux-x64.tar.gz |
Linux x86_64 |
macOS builds are ad-hoc signed but not notarized. The install script strips the Gatekeeper quarantine attribute for you. If you download a DMG directly through a browser and macOS refuses to open it, run:
xattr -dr com.apple.quarantine /Applications/Notilus.app
iOS / iPadOS are not currently distributed as prebuilt binaries — see Build from source.
- Start Ollama locally:
ollama serve ollama pull llama3.2 # or any model you prefer - Open Notilus. It points at
http://localhost:11434by default; the green dot in the top bar confirms it can reach the host. - Open Settings → Default Model and pick a model from the list.
- Optionally switch the appearance (
System/Light/Dark) and tweak temperature.
Running Ollama on a different machine? Open Settings and set the Host URL to e.g.
http://192.168.1.42:11434. Make sure Ollama is bound to that interface (OLLAMA_HOST=0.0.0.0:11434 ollama serve). The iOS app already includes the ATS exception and Local Network usage description required to reach it.
◀ ▶ Documents [▥|≣] ● llama3.2 ⚙
- Back / Forward — navigates the in-app history stack.
- Current folder name — just the basename (no breadcrumb clutter).
- Grid / List toggle — view-mode for the file area.
- Connection pill — green = Ollama reachable, red = unreachable. Click to open Settings.
- Settings gear — global preferences.
The pill collapses to just a dot when the main column is narrow; chrome reflows so the toggle and settings stay pinned at the right end.
Finder-style sidebar that extends edge-to-edge under the macOS traffic lights:
- System Overview — disk + folder analysis screen.
- Favorites — Home / Desktop / Documents / Downloads (only those that exist on the current platform).
- Locations — mounted volumes / drives (
/Volumes/*on macOS, drive letters on Windows,/media/<user>/*and/mnt/*on Linux,On My iPhoneon iOS). - Tags — placeholder section for future tag support.
In compact mode (phones / narrow windows) the sidebar collapses to a slide-in drawer toggled by the menu button.
A CupertinoSlidingSegmentedControl switches between:
- Info — preview, name, kind, size, modified, location of the current selection (Finder's "Get Info"-style panel).
- Chat — Ollama chat with an attach toggle on the selected file: text is extracted (text capped at 200 KB; PDFs and Office docs use external tools if available) and images are passed as base64 to a vision-capable model.
- Workflows — list, edit, and run saved prompt chains.
On compact widths these become tabs in the bottom tab bar (Files / Info / Chat / Flows) alongside the file area.
Quick-Look-style full-screen modal. Trigger:
- macOS / desktop: select a file, press Space.
- iOS / touch: single-tap a file (folders still navigate).
Per-type viewers:
| Type | Extensions | Viewer |
|---|---|---|
| Image | png jpg jpeg gif bmp webp heic tif |
InteractiveViewer + Image.file (pinch / drag zoom) |
| Markdown | md markdown mdown |
flutter_markdown rendered, with a toggle to view raw source |
| Text / code | txt json yaml xml csv html css js ts dart py go rs c cpp java kt swift sh toml ini conf log +more |
Monospaced, selectable, capped at 1 MB |
pdf |
pdfx (PDFKit on Apple, PDFium elsewhere) |
|
| Video | mp4 mov m4v mkv webm |
video_player with play / pause / seek overlay |
| Audio | mp3 wav m4a aac flac ogg |
just_audio with scrubber |
| Anything else | — | Info fallback with name / kind / size |
Top nav shows filename — n of total, plus prev / next buttons.
Open
Open With ▶ (macOS) → Default Application
Choose Application… (native AppleScript picker)
Share… (iOS)
─────────
Get Info
Rename…
Duplicate
Copy Path
Reveal in Finder / Open Parent Folder (iOS)
─────────
Move to Trash / Delete (iOS — hard delete; no sandbox Trash)
─────────
New Folder
Use Groups
Sort By ▶
Show View Options
| Action | Desktop | iOS / touch |
|---|---|---|
| Select file | Click | Tap |
| Open folder | Double-click | Tap |
| Preview file (Quick Look) | Space (after selecting) | Tap |
| Context menu | Right-click | Long-press |
| Cycle preview siblings | ← / → / Space | Swipe |
| Close preview | Esc | Back / swipe down |
| Toggle integrated terminal | ⌘J (macOS) / Ctrl+J | — |
Filesystem changes (files created, renamed, or deleted by another app) appear automatically — no refresh shortcut needed.
Notilus speaks the /api/generate streaming protocol. Defaults:
- Host:
http://localhost:11434 - Temperature: configurable in Settings (0.0 – 1.5)
- Model: chosen from the list returned by
/api/tags
# Install + start Ollama
brew install ollama # macOS; see ollama.com for other platforms
ollama serve
ollama pull llama3.2In Settings, hit Save & Test after editing the Host URL. The
"Default Model" picker is populated from /api/tags.
localhost on iOS means the phone itself, not your Mac. Set Host URL
to your Mac's LAN IP (e.g. http://192.168.1.42:11434) and run
Ollama bound to all interfaces:
OLLAMA_HOST=0.0.0.0:11434 ollama serveThe bundled ios/Runner/Info.plist already includes:
NSAppTransportSecurity → NSAllowsLocalNetworking(HTTP on LAN)NSLocalNetworkUsageDescription(iOS 14+ prompt)UIFileSharingEnabled+LSSupportsOpeningDocumentsInPlace(so the app's Documents folder shows up in the iOS Files app)
A workflow is an ordered list of WorkflowSteps. Each step has:
- name — display label
- prompt template — supports placeholders:
{file_content}— text contents of the selected file{file_name},{file_path}{prev}— previous step's output{step_1},{step_2}, … — output of any earlier numbered step
- model (optional) — overrides the default chat model for this step only
Workflows are saved as JSON via SettingsStore (shared_preferences)
and re-run from the Workflows tab on the home screen. Step-by-step
output streams into a results panel while the run is in flight.
lib/
├── app.dart # MultiProvider + CupertinoApp wiring
├── main.dart
├── theme.dart # AppPalette light/dark (pure neutral)
├── utils/
│ └── responsive.dart # 750-px compact breakpoint helper
├── models/
│ ├── file_entry.dart # FileSystemEntity + cached stat
│ ├── chat_message.dart # user / assistant + streaming flag
│ ├── workflow.dart
│ └── workflow_step.dart
├── providers/
│ ├── browser_provider.dart # history, watcher, sort, view mode
│ ├── chat_provider.dart # streaming chat state
│ ├── settings_provider.dart # theme, host, model, temperature
│ └── workflow_provider.dart # CRUD + run lifecycle
├── services/
│ ├── file_service.dart # list, drives, shortcuts (per-OS)
│ ├── file_actions_service.dart # open, open-with, rename, trash, …
│ ├── ollama_service.dart # /api/tags + /api/generate stream
│ ├── attachment_service.dart # text/PDF/Office → text; images → base64
│ ├── thumbnail_service.dart # disk-cached PDF/SVG/text thumbnails
│ ├── settings_store.dart # shared_preferences wrapper
│ ├── system_info_service.dart # disk usage + folder breakdown
│ └── workflow_runner.dart
├── screens/
│ ├── home_screen.dart # wide + compact layouts, top bar
│ ├── settings_screen.dart
│ ├── system_overview_screen.dart
│ ├── workflow_editor_screen.dart
│ └── file_preview_screen.dart # Quick-Look-style viewer (incl. Markdown)
└── widgets/
├── sidebar.dart # full-height; drawer in compact
├── breadcrumb_bar.dart # (legacy — currently unused)
├── path_status_bar.dart # Finder-style bottom status bar
├── file_list_view.dart # list view + context menu wiring
├── file_icon_grid.dart # icon view with cached thumbnails
├── desk_context_menu.dart # overlay menu with submenus
├── terminal_panel.dart # PTY-backed terminal with VSCode tabs
├── chat_panel.dart
├── workflow_tab.dart
├── workflow_run_view.dart
└── info_panel.dart
| File | Purpose |
|---|---|
macos/Runner/MainFlutterWindow.swift |
Transparent titlebar + contentMinSize 900 × 600 |
windows/runner/win32_window.cpp |
WM_GETMINMAXINFO enforces 900 × 600 (DPI-scaled) |
linux/runner/my_application.cc |
gtk_window_set_geometry_hints with GDK_HINT_MIN_SIZE |
ios/Runner/Info.plist |
ATS exception, file sharing, local network usage |
- State is plain
ChangeNotifier+provider— one provider per domain (browser / chat / settings / workflows). No Riverpod, no BLoC, no codegen. - Persistence is
shared_preferencesonly. No SQLite, no JSON files on disk for app data. - Networking is a single
http.Client+dart:asyncstream for Ollama. Token chunks are forwarded straight to the chat / workflow UI as they arrive. - Filesystem is
dart:io. Listing is shallow per directory; the current folder is watched withDirectory.watch(recursive: false)and changes are debounced before re-listing. Falls back silently on platforms / filesystems where watching isn't supported. - Theming is a small
AppPaletterecord with light / dark variants resolved viaCupertinoTheme.brightnessOf. Dark surfaces are pure-neutral (R = G = B); the only chromatic tokens are the accent (system blue), folder icon (lighter blue), success (green), and danger (red). - Responsiveness is a single 750-px breakpoint:
- ≥ 750 px → 3-pane wide layout (sidebar full-height, fluid panel widths)
- < 750 px → bottom-tab compact layout (Files / Info / Chat / Flows), sidebar becomes a slide-in drawer
- Flutter
>=3.10.0(Dart>=3.0.0) - A running Ollama instance (see above)
- macOS: Xcode + CocoaPods (for the macOS / iOS targets)
- Windows: Visual Studio with the Desktop development with C++ workload
- Linux:
clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev
flutter pub get
flutter run -d macos # or: linux, windows, ios, ipad, chromeflutter build macos # universal .app (arm64 + x64)
flutter build windows # build/windows/x64/runner/Release/
flutter build linux # build/linux/x64/release/bundle/
flutter build ios # archive in Xcode for distributionpubspec.yaml includes a dependency_overrides pin:
dependency_overrides:
video_player_avfoundation: 2.6.5video_player_avfoundation 2.8.x split its Apple plugin into a
modular Obj-C subtarget that unconditionally imports
<Flutter/Flutter.h>, which doesn't exist on macOS (macOS exposes
FlutterMacOS). Pinning to the last pre-split version restores the
macOS build; iOS is unaffected. Drop the override when the upstream
plugin fixes the issue.
Releases are produced by
.github/workflows/release.yml:
- Trigger: push a tag matching
v*or run the workflow manually with thetaginput. - Builds: macOS (universal + arm64 + x64 DMGs), Windows x64 zip, Linux x64 tarball — all in parallel.
- Publishes: a single GitHub Release named
Notilus <tag>with all five archives attached and auto-generated release notes.
Typical flow:
# 1. Bump version in pubspec.yaml (e.g. 0.1.5 → 0.1.6)
# 2. Commit + push
git commit -am "Bump version to 0.1.6"
git push
# 3. Tag and push the tag
git tag v0.1.6
git push origin v0.1.6The workflow resolves the tag from
workflow_dispatchinput orGITHUB_REF_NAME(in that order), so manual dispatches won't accidentally name the release after the branch.
| Package | Why |
|---|---|
provider |
App-wide state via ChangeNotifier |
http |
Ollama REST calls |
shared_preferences |
Local settings + saved workflows |
path, path_provider |
OS-specific path helpers |
pdfx |
PDF preview + first-page thumbnails |
video_player |
Video preview |
just_audio |
Audio preview |
flutter_markdown |
Rendered Markdown preview |
flutter_svg |
SVG thumbnails + icons |
xterm, flutter_pty |
Integrated PTY-backed terminal |
archive |
Reading zip/tar contents (e.g. legacy .docx text extraction fallback) |
share_plus |
iOS share-sheet for "Open With" |
cupertino_icons |
Icon font |
Dev-only: flutter_lints, flutter_launcher_icons.
Notilus is an early, single-developer project. Desktop targets (macOS / Linux / Windows) are the primary focus; iOS / iPadOS work but are limited by the iOS app sandbox (you can only browse the app's Documents folder and whatever you share into it from the Files app).
Things on the short list:
- Notarized macOS builds (currently ad-hoc signed)
- Tag support in the sidebar (UI is there; persistence isn't)
- File search inside the current folder
- Disk-cached raster thumbnails for raw images (PDFs / SVGs / text are already cached)
- Optional: bundle pinning for
share_plus11.x once it stabilises
PRs and issues welcome at https://github.com/JayashBhandary/Notilus.
No license has been added yet — see the repository for updates. Until a license is published, default copyright applies: the source is readable but not freely redistributable.