Skip to content

Latest commit

 

History

721 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

* Mova
:PROPERTIES:
:CUSTOM_ID: mova
:END:
A mobile client for [[https://github.com/colonelpanic8/org-agenda-api][org-agenda-api]] - access and manage your Emacs org-mode tasks from Android and iOS.

** Overview
:PROPERTIES:
:CUSTOM_ID: overview
:END:
Mova is a React Native/Expo application that connects to an org-agenda-api server, providing a native mobile interface for:

- Viewing your daily agenda with scheduled items and deadlines
- Capturing new tasks using org-mode capture templates
- Searching across all your TODO items
- Running custom agenda views
- Receiving notifications for upcoming tasks

** App Downloads
:PROPERTIES:
:CUSTOM_ID: app-downloads
:END:
On Android, add the Mova F-Droid repository to receive updates automatically:

- https://colonelpanic8.github.io/mova/

The phone and Wear OS APKs are also attached to each GitHub release:

- https://github.com/colonelpanic8/mova/releases/latest

The F-Droid repository serves the same signed phone APKs as the releases page,
so installs from either source upgrade in place. See
[[file:docs/fdroid.md][docs/fdroid.md]] for publication and repository setup details.

** Relationship to org-agenda-api
:PROPERTIES:
:CUSTOM_ID: relationship-to-org-agenda-api
:END:
Mova is the mobile frontend for [[https://github.com/colonelpanic8/org-agenda-api][org-agenda-api]], a JSON HTTP API that exposes org-mode data from GNU Emacs. The architecture:

#+begin_src mermaid :file architecture.png
flowchart TB
    subgraph Docker["Docker Container"]
        nginx["nginx<br/>(HTTP Basic Auth)"]
        subgraph Emacs
            httpd["simple-httpd<br/>(HTTP server)"]
            api["org-agenda-api<br/>(elisp)"]
            org["org-mode"]
            httpd --> api --> org
        end
        repo["Git Repository<br/>(.org files on disk)"]
        sync["git-sync-rs"]
        nginx --> httpd
        org <--> repo
        sync <--> repo
    end
    remote["Remote Git"]
    mova["Mova<br/>(this app)"]

    sync <--> remote
    mova <-->|HTTP/JSON| nginx
#+end_src

#+RESULTS:
[[file:architecture.png]]

- *nginx* handles HTTP Basic Auth and proxies requests to Emacs
- *simple-httpd* provides the HTTP server functionality within Emacs
- *org-agenda-api* is an elisp package that runs inside Emacs, translating org-mode data into JSON HTTP responses
- *org-mode* reads and writes =.org= files from the git repository on disk
- *git-sync-rs* keeps the local git repository synchronized with a remote
- *Mova* connects to nginx via HTTP Basic Auth to provide a native mobile experience

You need a running org-agenda-api instance for Mova to connect to. See the [[https://github.com/colonelpanic8/org-agenda-api][org-agenda-api README]] for setup instructions.

** Features
:PROPERTIES:
:CUSTOM_ID: features
:END:
*** Agenda View
:PROPERTIES:
:CUSTOM_ID: agenda-view
:END:
- Daily agenda with date navigation
- Shows scheduled items, deadlines, and overdue tasks
- Pull-to-refresh synchronization
- Inline task completion and editing

*** Capture
:PROPERTIES:
:CUSTOM_ID: capture
:END:
- Template-based task capture using your org-mode capture templates
- Dynamic form fields (text, dates, tags) defined by templates
- Priority and TODO state selection
- Scheduled/deadline date pickers
- Remembers your last-used template

*** Search
:PROPERTIES:
:CUSTOM_ID: search
:END:
- Full-text search across all TODO items
- Searches title, tags, and TODO state
- Real-time filtering

*** Custom Views
:PROPERTIES:
:CUSTOM_ID: custom-views
:END:
- Access your custom org-agenda commands
- Dynamic view rendering from server-defined views

*** Android Widgets
:PROPERTIES:
:CUSTOM_ID: android-widget
:END:
- Quick Capture widget for home screen
- Capture tasks without opening the app
- Configurable template per widget instance
- Agenda widget listing today's items (overdue first), each with a
  tap-to-complete circle that finishes the item without opening the app, plus
  task/habit tabs and typed and voice quick-capture actions in the header;
  scheduled/deadline timestamps appear on cards, with two cards per row at
  wider sizes

*** Intents API (Android)
:PROPERTIES:
:CUSTOM_ID: intents-api
:END:
- =mova://= links let any app or assistant create, complete, update, delete
  and open todos, run a search, jump to a date, or open the capture dialogs
- A read-only content provider exposes todo search and the agenda to apps
  that hold the =READ_TODOS= runtime permission
- Every action, parameter and value format is documented in
  [[file:docs/intents.md][docs/intents.md]]

*** Notifications
:PROPERTIES:
:CUSTOM_ID: notifications
:END:
- Background sync for upcoming tasks
- Configurable notification intervals
- Scheduled and deadline reminders

** Getting Started
:PROPERTIES:
:CUSTOM_ID: getting-started
:END:
*** Prerequisites
:PROPERTIES:
:CUSTOM_ID: prerequisites
:END:
- Node.js 18+
- Yarn or npm
- A running [[https://github.com/colonelpanic8/org-agenda-api][org-agenda-api]] server
- For development: Android Studio (Android) or Xcode (iOS)

*** Installation
:PROPERTIES:
:CUSTOM_ID: installation
:END:
#+begin_src sh
# Clone the repository
git clone https://github.com/colonelpanic8/mova
cd mova

# Install dependencies
yarn install

# Start the development server
yarn start
#+end_src

*** Running the App
:PROPERTIES:
:CUSTOM_ID: running-the-app
:END:
#+begin_src sh
# Android
yarn android

# iOS
yarn ios

# Or use Expo Go for quick testing
npx expo start
#+end_src

*** Connecting to org-agenda-api
:PROPERTIES:
:CUSTOM_ID: connecting-to-org-agenda-api
:END:
1. Launch Mova
2. Enter your org-agenda-api server URL (e.g., =https://your-server.com=)
3. Enter your username and password
4. Tap Login

** Development
:PROPERTIES:
:CUSTOM_ID: development
:END:
*** Project Structure
:PROPERTIES:
:CUSTOM_ID: project-structure
:END:
#+begin_example
mova/
├── app/                    # Expo Router screens
│   ├── (tabs)/             # Main tab navigation
│   │   ├── index.tsx       # Agenda screen
│   │   ├── capture.tsx     # Capture screen
│   │   ├── search.tsx      # Search screen
│   │   ├── habits.tsx      # Habits screen
│   │   ├── views.tsx       # Custom views
│   │   └── settings/       # Settings screens
│   ├── edit.tsx            # Todo editor
│   └── login.tsx           # Login screen
├── services/               # API client, query cache, background tasks
│   ├── api.ts              # org-agenda-api client
│   ├── queryClient.ts      # TanStack Query client + offline persistence
│   ├── captureOutbox.ts    # Durable offline capture queue
│   ├── backgroundSync.ts   # Background task registration
│   └── notifications.ts    # Local notifications
├── hooks/                  # Query hooks (useAgendaData, queryKeys), mutations
├── components/             # Reusable UI (agenda/, capture/, todoForm/, todoEditing/)
├── context/                # React context providers (auth, settings, filters)
├── widgets/                # Android home-screen widget (JS side)
├── android/wear/           # Wear OS companion app (Kotlin)
└── tests/                  # Jest unit/integration/component tests
#+end_example

*** Commands
:PROPERTIES:
:CUSTOM_ID: commands
:END:
#+begin_src sh
yarn start          # Start Expo dev server
yarn android        # Run on Android
yarn ios            # Run on iOS
yarn test           # Run Jest tests
yarn typecheck      # TypeScript validation
yarn lint           # ESLint check
yarn e2e:android    # Run Detox E2E tests
#+end_src

*** Testing
:PROPERTIES:
:CUSTOM_ID: testing
:END:
#+begin_src sh
# Unit tests
yarn test

# E2E tests (requires Android emulator)
yarn e2e:build:android
yarn e2e:android
#+end_src

** Tech Stack
:PROPERTIES:
:CUSTOM_ID: tech-stack
:END:
- *React Native* + *Expo* - Cross-platform mobile framework
- *Expo Router* - File-based navigation
- *React Native Paper* - Material Design 3 components
- *TanStack Query* - Server-state caching, invalidation, and offline
  persistence (queries are keyed per server identity and persisted to
  AsyncStorage for offline launches)
- *TypeScript* - Type safety
- *Jest* + *Detox* - Testing

Note: the home-screen widget, quick-capture activity, and Wear OS
companion app are Android-only; iOS currently has no counterpart for
these capture surfaces.

The Wear OS companion also provides a =Mova Quick Capture= Tile. Add it
from the watch's tile picker to launch voice capture directly by swiping
from the watch face and tapping =Capture=. A second assistant button uses
system dictation to send a text instruction to OpenAI, then lets the model
read or change org data through an allowlisted set of org-agenda-api tools.
Raw microphone audio is handled by the Wear OS speech recognizer and is not
sent to OpenAI by Mova.

** Configuration
:PROPERTIES:
:CUSTOM_ID: configuration
:END:
*** Watch Assistant
:PROPERTIES:
:CUSTOM_ID: watch-assistant
:END:

To enable natural-language requests from Wear OS:

1. Open Mova's Settings screen on the phone.
2. Under =Watch Assistant=, enter an OpenAI API key and model.
3. Tap =Save= while the watch is paired.

The default model is =gpt-5.6-luna= for a latency/cost-sensitive watch
workflow. The OpenAI key is kept in secure storage on the phone and in
encrypted preferences on the watch. Assistant requests go directly from the
watch to OpenAI's Responses API; tool calls then go directly from the watch to
the configured org-agenda-api server.

The assistant can search and count todos, inspect agendas and habits, capture,
update, complete, or explicitly delete tasks, and use other allowlisted API
operations. It resolves mutation targets to an org ID or exact file position
before acting and asks for clarification rather than guessing when a target is
ambiguous.

*** Capture Templates
:PROPERTIES:
:CUSTOM_ID: capture-templates
:END:
Capture templates are defined in your Emacs org-mode configuration and exposed via the org-agenda-api =/capture-templates= endpoint. Mova automatically fetches and renders forms based on your template definitions.

Example template structure from org-agenda-api:

#+begin_src json
{
  "todo": {
    "name": "Todo",
    "prompts": [
      { "name": "title", "type": "string", "required": true },
      { "name": "scheduled", "type": "date", "required": false },
      { "name": "tags", "type": "tags", "required": false }
    ]
  }
}
#+end_src

*** Notifications
:PROPERTIES:
:CUSTOM_ID: notifications-1
:END:
Configure notification preferences in Settings:

- Enable/disable notifications
- Set reminder intervals (e.g., 15 minutes before)
- Background sync frequency (default: 15 minutes)

** License
:PROPERTIES:
:CUSTOM_ID: license
:END:
GPL v3

About

Mobile org-mode via API

Resources

Stars

10 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages