Skip to content
Merged
Show file tree
Hide file tree
Changes from 73 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
bb81fa7
docs(mobile): add standalone offline-first mobile app plan
ddutchie Jul 2, 2026
40b4d6d
feat(sync): offline-first sync engine foundation (P0 spike + P1 schema)
ddutchie Jul 2, 2026
f0d8b4d
refactor(sync): extract portable sync engine into shared/ (P2)
ddutchie Jul 2, 2026
e2cfd8f
feat(mobile): Expo SDK 57 skeleton + local DB + read-only sync-in (P3)
ddutchie Jul 2, 2026
65714f7
fix(mobile): consume shared engine as @cairn/shared package via symlink
ddutchie Jul 2, 2026
4ce121e
chore(mobile): use expo run:ios/android dev-build scripts
ddutchie Jul 2, 2026
47685bf
fix(mobile): use UILaunchScreen instead of splash storyboard (config …
ddutchie Jul 5, 2026
e8e8667
revert(mobile): drop UILaunchScreen workaround, restore storyboard sp…
ddutchie Jul 5, 2026
5e61e8a
feat(mobile): native tabs + safe areas + app icon + device signing
ddutchie Jul 5, 2026
3e02f53
feat(mobile): P4 bidirectional sync over an iCloud folder + note editing
ddutchie Jul 5, 2026
e62eb29
feat(sync): desktop sync engine wiring + backfill + Device Sync settings
ddutchie Jul 5, 2026
474b7e2
feat(sync): write .md files on sync-in (desktop dual-write parity)
ddutchie Jul 5, 2026
93aea0d
feat(mobile): project hierarchy + markdown rendering + shared theme
ddutchie Jul 5, 2026
201cd17
fix(markdown): escape unknown <angle-bracket> tags instead of crashing
ddutchie Jul 5, 2026
5a53f82
feat(mobile): desktop-parity project icons, folder tree, interactive …
ddutchie Jul 5, 2026
685f8a2
feat(mobile): AI chat with tool-calling note/task editing (Rork, P5)
ddutchie Jul 5, 2026
ebe4a29
feat(mobile): streaming /agent/chat with native tool-calling + fixes
ddutchie Jul 5, 2026
8950dc4
fix(mobile): chat composer no longer leaves dead space when keyboard …
ddutchie Jul 5, 2026
d1c5fe1
feat(mobile): smooth keyboard handling via react-native-keyboard-cont…
ddutchie Jul 5, 2026
b389eff
fix(mobile): use app's iCloud container for sync (durable, no picker)
ddutchie Jul 5, 2026
93d2b72
fix(mobile): robust @cairn/shared resolution (npm install wiped the s…
ddutchie Jul 5, 2026
517ea35
fix(mobile): revert raw-filePath resolver (broke Metro SHA-1)
ddutchie Jul 5, 2026
ca5fb7b
style(mobile): chat bubbles + tool chips match desktop ChatMessageBubble
ddutchie Jul 5, 2026
7a64d9a
feat(mobile): richer chat tools — get_cairn_context + get_project_con…
ddutchie Jul 6, 2026
728bde8
feat(mobile): drop redundant list_projects tool so agent uses get_cai…
ddutchie Jul 6, 2026
e608893
feat(mobile): Liquid Glass input bars for chat + search (expo-glass-e…
ddutchie Jul 6, 2026
27dfd6b
style(mobile): taller chat glass input + more bottom breathing room
ddutchie Jul 6, 2026
5133149
feat(mobile): markdown parity (wikilinks/callouts), chat note links, …
ddutchie Jul 6, 2026
7246b54
feat(mobile): surface sync conflicts + resolution UI, display tags
ddutchie Jul 6, 2026
1144d02
feat(mobile): chat image attachments + create notes & cards
ddutchie Jul 6, 2026
1ad413e
feat(mobile): automatic foreground sync + conflict banner
ddutchie Jul 6, 2026
cd01942
fix(sync): eliminate false-positive conflicts + compact oplog
ddutchie Jul 6, 2026
799fe67
feat(mobile): persist chat locally (on-device only) + clear action
ddutchie Jul 6, 2026
b5a2f3c
fix(sync): migrate legacy sync_row_base (base_hlc) table
ddutchie Jul 6, 2026
1056dc2
fix(sync): stop deleted notes resurrecting via file-watcher re-import
ddutchie Jul 6, 2026
739dc2b
refactor(shared): extract formatters + stripMarkdown, dedupe tool-label
ddutchie Jul 6, 2026
1adf2a7
feat(mobile): design-system polish, native tab headers, board fix
ddutchie Jul 6, 2026
12d9d71
feat(mobile): chat scroll-behind composer + desktop-style note rows
ddutchie Jul 6, 2026
3d65a1d
refactor(mobile): move Sync from a tab to a header badge entry point
ddutchie Jul 6, 2026
c5996eb
feat(mobile): note/card management — pin, delete, archive, filter
ddutchie Jul 6, 2026
f33e761
feat(mobile): accent tab tint, scroll-minimizing tab bar, search auto…
ddutchie Jul 6, 2026
efe43aa
feat(mobile): field editors — tags, due date, assignee + task search
ddutchie Jul 6, 2026
580531d
fix(mobile): due-date picker — confirm-based sheet, selectable today,…
ddutchie Jul 6, 2026
2420af5
feat(mobile): drag-and-drop kanban board
ddutchie Jul 6, 2026
339db50
refactor(shared): hoist syntax palette + checkbox/colour helpers to s…
ddutchie Jul 6, 2026
611d114
feat(mobile): markdown rendering parity with desktop
ddutchie Jul 6, 2026
7209641
fix(mobile): task-list checkboxes render like desktop
ddutchie Jul 6, 2026
989562b
feat(mobile): KaTeX math + Mermaid diagrams via offline WebView
ddutchie Jul 6, 2026
e1d00f2
chore(mobile): drop unused React import, explicit ReactNode type
ddutchie Jul 6, 2026
a3dcaf9
fix(mobile): callout header stripping + list bullet markers
ddutchie Jul 6, 2026
66ca38c
feat(shared): pure markdown format transforms + AI action prompts
ddutchie Jul 6, 2026
72ce15e
feat(mobile): note editor formatting + AI toolbar
ddutchie Jul 6, 2026
c43d7fd
fix(mobile): note toolbar safe-area + scroll affordances
ddutchie Jul 6, 2026
9ce1ca6
chore(mobile): stop committing signing team ID + add env template
ddutchie Jul 6, 2026
7f2085f
feat(mobile): pluggable AI provider (Rork + OpenAI-compatible)
ddutchie Jul 6, 2026
aa94434
chore(mobile): add RN-aware ESLint config
ddutchie Jul 6, 2026
0ef5a76
style(mobile): fix lint issues surfaced by expo eslint
ddutchie Jul 6, 2026
4b06cf6
feat(mobile): AI settings sheet with provider toggle + model picker
ddutchie Jul 6, 2026
5ed1147
fix(mobile): keyboard no longer covers modal sheet content
ddutchie Jul 6, 2026
92a22f4
fix(mobile): ensure @cairn/shared symlink at bundle time
ddutchie Jul 6, 2026
ca9488d
fix(sync): don't build the oplog until Device Sync is enabled
ddutchie Jul 6, 2026
4a82c75
docs: v2.4.0 changelog + require mobile app in Device Sync copy
ddutchie Jul 6, 2026
d3fcf3f
refactor(mobile): native Stack.Toolbar headers + root ThemeProvider
ddutchie Jul 6, 2026
ca7692c
feat(mobile): light/dark splash screen
ddutchie Jul 6, 2026
0174c08
Update SyncSettings.tsx
ddutchie Jul 6, 2026
b343bf7
ci(mobile): EAS TestFlight release pipeline + expo-doctor fixes
ddutchie Jul 6, 2026
c66e1e8
feat(mobile): OTA updates via EAS Update with apply banner
ddutchie Jul 6, 2026
96bc5a2
fix(mobile,sync): correctness fixes from PR review (batch 1)
ddutchie Jul 7, 2026
de8cc00
fix(mobile,shared): staleness + logic fixes from PR review (batch 2)
ddutchie Jul 7, 2026
c8c35f4
perf(mobile): memoize render-time DB reads + debounce search (batch 3)
ddutchie Jul 7, 2026
0d2191d
refactor: address PR review nits — style, a11y, docs, types (batch 4)
ddutchie Jul 7, 2026
1366750
refactor(sync,mobile): larger PR-review items (batch 5)
ddutchie Jul 7, 2026
f27bf2f
fix(shared): keep due-date/compact-date status LOCAL, not UTC
ddutchie Jul 7, 2026
3ca63bc
chore: drop redundant tsconfig.shared-node include entries
ddutchie Jul 7, 2026
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
72 changes: 72 additions & 0 deletions .github/workflows/release-mobile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Release Mobile (TestFlight)

# Builds the Expo iOS app on EAS and submits it to TestFlight.
#
# Trigger: push a tag like `mobile-v2.4.0` (see scripts/releasemobile.sh), or
# run manually from the Actions tab (workflow_dispatch).
#
# Required repo secret:
# EXPO_TOKEN — an Expo access token (expo.dev → Account → Access tokens).
# EAS uses it for both the build and the TestFlight submit;
# Apple signing credentials + the App Store Connect key are
# stored on EAS (managed credentials), not here.
#
# One-time setup (run locally in mobile/, see mobile/README):
# npm i -g eas-cli && eas login && eas init && eas credentials

on:
push:
tags:
- "mobile-v*"
workflow_dispatch:
inputs:
submit:
description: "Submit the build to TestFlight after it finishes"
type: boolean
default: true

permissions:
contents: read

jobs:
build-ios:
name: EAS Build + Submit (iOS)
runs-on: ubuntu-latest
# The build itself runs on EAS's macOS workers; this runner only drives the
# eas-cli, so ubuntu is fine (and cheaper/faster to spin up).
defaults:
run:
working-directory: mobile

steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
with:
persist-credentials: false

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: 22
cache: npm
cache-dependency-path: mobile/package-lock.json

# Sets up eas-cli and authenticates via EXPO_TOKEN.
# NOTE: pin to a commit SHA once you've vetted the version you want.
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}

- name: Install dependencies
run: npm ci

# Build on EAS (managed credentials) and wait for the artifact.
- name: EAS Build (iOS, production)
run: eas build --platform ios --profile production --non-interactive --wait

# Submit the finished build to TestFlight. Skipped if a manual run
# unchecked "submit". Uses the latest successful iOS build for this commit.
- name: EAS Submit (TestFlight)
if: >-
github.event_name == 'push' ||
github.event.inputs.submit == 'true'
run: eas submit --platform ios --profile production --latest --non-interactive
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ yarn-error.log*

# env files (can opt-in for committing if needed)
.env*
# ...but always track example/template env files (no secrets).
!.env.example
!**/.env.example

# vercel
.vercel
Expand Down Expand Up @@ -82,3 +85,4 @@ cairn.db-wal
# test mock vaults
/tests/fixtures/obsidian-vault/
scripts/runtime-baselines/
/mobile/node_modules
19 changes: 19 additions & 0 deletions changelogs/v2.4.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## What's new

This release introduces the **Cairn mobile companion app** and the desktop-side **Device Sync** that connects the two. The mobile app is an offline-first iOS/Android client (Expo) you build yourself today, with the App Store as a possible future path.

### Features

- **Cairn mobile companion app**: A new offline-first mobile client for your workspace — notes, tasks, a drag-and-drop board, folders, tags, and an AI chat/assistant. Markdown rendering has parity with the desktop (syntax highlighting, task-list checkboxes, callouts, KaTeX math, and Mermaid diagrams). Everything works offline; changes reconcile with the desktop when connected.
- **Device Sync (desktop)**: New **Settings → Device Sync** panel to connect a shared cloud folder (iCloud Drive, Dropbox, or Syncthing) that your phone also points at. Sync is offline-first and file-safe — only append-only oplog files ever transit the folder, never your database. On first connect, your entire existing workspace is seeded to the folder; after that only changes are exchanged, in the background.
- **Conflict-safe reconciliation**: Concurrent edits to the same note on two devices are resolved with last-writer-wins plus a preserved **"conflicted copy"** note, so an edit is never silently lost. Note edits arriving from the phone are re-projected to their `.md` files on disk, keeping the desktop's dual-write parity.
- **Pluggable mobile AI backend**: Mobile chat runs through a provider abstraction — a built-in endpoint for first-party builds, or any **OpenAI-compatible** endpoint (OpenAI, Azure OpenAI, OpenRouter, Together, Groq, LM Studio, Ollama, …) that you configure in-app with a base URL, model (with a "fetch models" picker), and an API key stored in the device keychain.

### Changes

- **Shared "one brain" core**: Note formatting, markdown transforms, folder-tree building, the syntax palette, and the offline-first sync engine were hoisted into a shared, framework-agnostic package used by both desktop and mobile — so the two apps stay behaviourally identical from a single source of truth.
- **Device Sync stays idle until enabled**: The desktop only builds and drains its sync oplog while a sync folder is connected. If you never enable Device Sync, no oplog is accumulated — there's zero overhead for desktop-only users.

### Requirements & notes

- **Device Sync requires the mobile app.** It exchanges changes with your phone, not with another desktop — the Settings panel now states this explicitly. The mobile app is currently a build-it-yourself companion (see the mobile README); there's nothing to sync with until it's installed on your device.
Loading