Building in public, one commit at a time.

GitScribe is an offline-first Markdown note editor with Git at its core. Follow along as we build it - every feature, every fix, and every lesson we learn along the way.

Performance Optimizations

We spent this cycle obsessed with optimizations. The app was working well, but as repositories got larger, we started seeing some jitter. We went through the entire application to isolate UI rebuilds, replacing heavy layouts with streamlined custom painters, and swapping out eagerly loaded columns for lazy list builders. The result is a buttery smooth experience even with hundreds of branches or files.

GitScribe now has a "Bottom Sheet First" design policy. Instead of disruptive modal dialogs or pushing new screens for simple settings, all contextual inputs now slide up elegantly from the bottom. Destructive operations (like discarding changes or deleting files) now use non-blocking undo snackbars instead of scary confirmation dialogues.

We also shipped an AI-generated commit messages feature using on-device AI if available. It analyzes the exact diff of the pending changes and proposes a clean, conventional commit message with one tap. And since everything is processed locally, nothing ever leaves users' devices.

v0.5.0
// What shipped
  • Complete UI audit enforcing a "Bottom Sheet First" mobile design policy
  • Major performance boost for the commit timeline using custom painters
  • Lazy loading implemented across the entire application
  • External file system changes are now reliably detected on app resume
  • Destructive actions replaced with safe, non-blocking undo snackbars
  • AI commit messages, analyzing diffs instantly

Per-file sync & connectivity

The sync tab got a big upgrade. Each file now shows its individual sync status - synced, modified, or pending sync. That last one was tricky: "pending sync" means a user has committed locally but hasn't pushed yet, so the file blinks amber to remind them there's work waiting to go upstream.

The sync state tracker now knows about lastSavedAt, lastSyncedAt, and hasUnpushedCommits separately, so the UI can show users exactly where their data is in the pipeline. The sync tab surfaces those last-saved and last-synced timestamps right at the top.

Connectivity got more visible too. The offline banner drops in from the top the moment the network goes away, and pull-to-refresh is wired up across the file browser and sync screens so users can always nudge things along by hand.

v0.4.4
// What shipped
  • Per-file sync status: synced, modified, pending sync (blinking amber)
  • Sync state tracks lastSavedAt, lastSyncedAt, and hasUnpushedCommits
  • Sync tab shows "Last Saved" and "Last Synced" timestamps
  • Pull-to-refresh across file browser and sync screens
  • Offline banner drops in on connectivity changes
  • History screen no longer shows loading skeleton on pull-to-refresh

Multi-repo management

GitScribe now supports multiple repositories. Users can add as many repos as they want, switch between them, and each one tracks its own sync state independently. The repo list shows each one's last sync time and status, and switching is instant because we keep the file trees cached.

Setting up a new repo is a lot less friction too. A user's Git profile is read once at auth time and pre-fills the Git author name and email, so users are not staring at an empty form trying to remember what email is on their commits.

The bundled sample vault used to commit with empty author strings if users poked at it before configuring anything - it now uses a sensible default author so those first commits are actually attributable.

v0.4.3
// What shipped
  • Add, switch, list, and delete repositories from settings
  • Each repo tracks its own sync state independently
  • Cached file trees for instant repo switching
  • Per-repo last-sync timestamps in the repo list
  • GitHub profile auto-populates Git author name and email
  • Sample vault uses a sensible default author (no empty-author commits)

Files, Search & Editor

Three areas got attention this week: the file browser, search, and the editor itself.

On the files side, the old single FAB grew up into a speed dial; tap the + and it fans out into options for new file and new folder. File selection now has long-press to enter selection mode, with the app bar transforming into a contextual action bar for rename and delete.

Search is a proper feature now. A Material 3 search bar gives instant results across the content of every file in user's repo, and tapping it drops the user straight into the editor at the right spot.

The editor now features keyboard shortcuts - Ctrl+B for bold, Ctrl+I for italic, Ctrl+K for links - plus YAML frontmatter support and inline rendering of local images. We switched the Markdown preview to flutter_markdown_plus with Material 3 styling after the original package was discontinued. External file changes (like editing from a computer and syncing) get detected and trigger a reload prompt.

v0.4.2
// What shipped
  • Speed dial FAB with animated + → x rotation and scale transitions
  • Contextual app bar with long-press multi-select
  • Shared widgets: EmptyStateWidget, InlineErrorBanner, AppDialogs
  • Material 3 SearchBar at /search with instant full-text results
  • Full-text search across the content of all files in the repository
  • Keyboard shortcuts: Ctrl+B (bold), Ctrl+I (italic), Ctrl+K (link)
  • YAML frontmatter support in editor and preview
  • Local image preview renders inline in Markdown
  • External change detection with reload prompt

Adaptive navigation & biometric lock

Restructured the entire navigation model. GitScribe now has four proper tabs: Files, Repository, Sync, and Settings. The navigation adapts to screen size; phones get a Material 3 bottom navigation bar, and tablets or foldables get a Material 3 navigation rail on the side.

We also added biometric lock, with a configurable grace period so it doesn't nag users if they just switched apps for a second. Both enabling and disabling the option requires a biometric confirmation, keeping users' notes and repositories safe.

Privacy policy, along with Terms of Use can now be accessed in Settings. A couple other configurable options, such as Auto-Sync intervals, and Hidden Files toggles, have also been added.

v0.4.1
// What shipped
  • 4-tab navigation: Files, Repository, Sync, Settings
  • Adaptive layout: NavigationBar (compact) + NavigationRail (medium+)
  • Biometric lock with configurable grace period (immediate to 30 minutes)
  • Settings now includes Privacy Policy and Terms of Use links
  • Auto-sync with configurable intervals (5 min to 1 hour)
  • Offline-aware sync: commits locally when offline, full sync when back online

Phase 2 - Motion & Material 3 overhaul

Phase 2 was supposed to be "just polish" but it ended up being bigger than Phase 1. Turns out the gap between "it works" and "it feels good" is enormous so we decided to split it into multiple sprints instead.

For this sprint, we completely reworked how GitScribe feels. Every animation now uses spring physics instead of linear easing - which sounds like a nerdy distinction but the difference is night and day. Things feel alive now. Bouncy where they should be, crisp where they shouldn't.

The whole motion system lives in a single AppMotion class with three spring profiles: snappy for taps and toggles, standard for most transitions, and gentle for ambient elements. Everything respects the system's reduce-motion preference too.

Also did a full Material 3 shape and color audit. Replaced every hardcoded Colors.black and Colors.grey with proper theme tokens. Dynamic color is now enabled on Android 12+ so it picks up user's wallpaper's palette. Turns out that feature was disabled this whole time.

v0.4.0
// What shipped
  • Spring-based animation system with snappy, standard, and gentle profiles
  • Staggered file list animations with slide, scale, and fade per item
  • Long-press micro-interaction with haptic feedback
  • Dynamic color enabled on Android 12+
  • Full Material 3 shape token audit - AppRadius aligned to MD3 spec
  • All hardcoded colors replaced with semantic colorScheme tokens
  • Accessibility: reduce-motion support across all animated widgets
  • Semantics labels added to status dots, file cards, sync indicator, and nav bar

File history & version restore

One of the things that makes Git-backed notes genuinely useful: the ability to restore any version. We've built a file history screen that shows every commit that touched a given file as a timeline, and whether it's been synced to the remote or is still local-only.

Tapping a commit opens a diff view in a bottom sheet with a line-by-line comparison using an LCS diff algorithm implemented in Dart. Green for additions, red for deletions. There's a "Restore" button that reverts the file to that exact version and pops the user back into the editor.

The Git operations are all in Rust: git_log, git_show_file, and git_remote_head_sha to determine the synced/local badge. Having the heavy lifting in native code means the history screen loads instantly even for files with hundreds of commits.

v0.3.0
// What shipped
  • File history timeline with synced/local badges per commit
  • Diff bottom sheet with LCS-based line comparison
  • One-tap restore to any previous version
  • Rust-powered git_log, git_show_file, git_remote_head_sha
  • Editor reloads after history restore

Phase 1 - the core loop

The core loop is complete: authenticate → clone a repo → browse files → edit Markdown → sync back to remote. We've been dogfooding it for our own project notes and it's already changed how we take notes. Having real version history on our phones without having to think about it is genuinely useful.

Authentication supports GitHub Device Flow and a manual PAT option for any Git provider. GitLab OAuth with PKCE is implemented but the deep link callback needs App Links verification on a real device.

The sync engine is something we're particularly proud of. It runs through a SyncOrchestrator that handles the full pull → merge → commit → push cycle. When offline, it gracefully degrades to commit-only mode and syncs when connectivity returns. All Git operations are serialized through a queue to prevent corruption.

v0.2.0
// What shipped
  • GitHub Device Flow authentication
  • Clone with real-time progress streaming from Rust FFI
  • File browser with folder navigation, breadcrumbs, and hidden directory filtering
  • Markdown editor with syntax highlighting, auto-save (500ms debounce), and atomic writes
  • Sync engine: pull → merge → commit → push with offline fallback
  • Git operations serialized through a single-writer queue
  • Token refresh for GitLab, re-auth detection for GitHub

The foundation

Before building any features, we decided to spend two weeks on infrastructure. We decided on Flutter with Rust via flutter_rust_bridge for the Git operations. Clean architecture with core, domain, data, and presentation layers. Riverpod 3.x for state management. go_router for navigation. The whole thing compiles to a single APK with native Rust code embedded.

The Git service wraps git2 with catch_unwind on every FFI export so a panic in Rust doesn't take down the Flutter app. Ten functions exposed to Dart: clone, pull, push, commit, status, add, log, show file, remote head SHA, and branch operations. All HTTPS-only - SSH support would double the complexity for a use case most phone users don't need.

v0.1.0
// What shipped
  • Flutter + Rust (flutter_rust_bridge 2.12.0) project scaffolding
  • Clean Architecture: core / domain / data / presentation layers
  • Riverpod 3.x state management with code generation
  • 10 Rust FFI functions with catch_unwind safety
  • Git operation queue with single-writer serialization and cancellation
  • Migration service with version-based runners
  • Material 3 theme scaffolding with dark/light/system support
  • Edge-to-edge display, ProGuard config, crash zone error boundary

The idea

There are note apps that sync via Git but they either treat Git as an afterthought, don't work offline properly, or bury the version history so deep users forget it's there. We wanted something that puts Git front and center: every file in a real repo, every change is a real commit, and users can see the full history of any file with one tap.

And that's how GitScribe was born. It's a Markdown editor that clones a user's repo, lets users browse and edit files, and syncs everything back. Offline-first, Material 3 design, the Git operations run in Rust because life's too short for slow clones on mobile. Is this a good idea? We have absolutely no idea. But we're going to build it anyway and write about the process here.