Daily Feature

Voice-first journaling with real-time streaming transcription, Omi wearable integration, and AI-powered reflections. The most complex audio processing in the app.

66
Files
~23,100
Lines of Code
5
Entry Types
12
Services

Module Structure

daily/
├── journal/     (31 files, ~8,300 lines)  - Core journaling
│   ├── models/          - JournalEntry, JournalDay, AgentOutput
│   ├── services/        - JournalService, ReflectionService
│   ├── providers/       - Riverpod state management
│   ├── screens/         - JournalScreen (2,156 lines)
│   └── widgets/         - EntryCard, InputBar, AudioPlayer
│
├── recorder/    (32 files, ~12,500 lines) - Audio & transcription
│   ├── models/          - OmiDevice
│   ├── services/        - Transcription, Omi, background recording
│   │   ├── transcription/   - LocalAgreement-2 streaming
│   │   └── omi/             - Bluetooth device integration
│   ├── providers/       - Recording state
│   └── widgets/         - Audio controls, debug overlay
│
├── capture/     (4 files, ~750 lines)     - Photo & handwriting
│   ├── services/        - PhotoCaptureService
│   └── screens/         - HandwritingScreen
│
└── search/      (3 files, ~750 lines)     - Journal search
    ├── services/        - Text search
    └── screens/         - Search UI

Streaming Transcription Pipeline

Three-stage pipeline with VAD-based auto-pause and LocalAgreement-2 text stability:

┌───────────────────────────────────────────────────────────────────────┐
│                    STREAMING TRANSCRIPTION PIPELINE                    │
│                                                                        │
│  Stage 1: Audio Capture                                                │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │ Microphone → SimpleNoiseFilter → SmartChunker (VAD) → Buffer    │  │
│  │                                                                  │  │
│  │ • 16kHz, 16-bit PCM, mono                                       │  │
│  │ • Noise filter pre-processing                                   │  │
│  │ • VAD detects speech vs silence                                 │  │
│  │ • Rolling buffer (30 seconds max)                               │  │
│  └─────────────────────────────────────────────────────────────────┘  │
│                              │                                         │
│                              ▼                                         │
│  Stage 2: Transcription                                                │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │ Re-transcribe every 3s during speech                            │  │
│  │                                                                  │  │
│  │ iOS/macOS: FluidAudio (Parakeet v3 CoreML) - faster             │  │
│  │ Android:   Sherpa-ONNX (Parakeet v3 ONNX) - v1.12.20 pinned    │  │
│  └─────────────────────────────────────────────────────────────────┘  │
│                              │                                         │
│                              ▼                                         │
│  Stage 3: LocalAgreement-2                                             │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │ Compare consecutive transcriptions                               │  │
│  │                                                                  │  │
│  │ ┌──────────────┐  ┌──────────────┐  ┌──────────────┐           │  │
│  │ │  Confirmed   │  │  Tentative   │  │   Interim    │           │  │
│  │ │ (2+ matches) │  │ (1 match)    │  │  (changing)  │           │  │
│  │ └──────────────┘  └──────────────┘  └──────────────┘           │  │
│  │                                                                  │  │
│  │ Display: [confirmed] [tentative] [interim]                      │  │
│  └─────────────────────────────────────────────────────────────────┘  │
│                              │                                         │
│                              ▼                                         │
│  Finalization (on VAD silence ≥1s or stop)                            │
│  ┌─────────────────────────────────────────────────────────────────┐  │
│  │ Flush buffer with 2s silence → Final transcription              │  │
│  │ Create JournalEntry with audio path + transcribed text          │  │
│  └─────────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────────┘

Key Configuration

Setting Value Purpose
Rolling Buffer Max 30 seconds 480k samples at 16kHz
Re-transcribe Interval 3 seconds During speech detection
VAD Silence Threshold 1 second Detect end of speech
Min Chunk Duration 500ms Efficiency
Energy Threshold 100-800 Speech vs silence (adjustable)
Important: Sherpa-ONNX is pinned to v1.12.20. Version v1.12.21 crashes on ARM (MediaTek chipsets).

Journal Entry Types

Voice

Audio recording with transcription. Stored as WAV file with text content.

  • Audio path in YAML frontmatter
  • Duration tracking
  • Transcription status

Text

Typed journal entries. Plain markdown content.

  • Direct text input
  • Markdown formatting
  • Created timestamp

Photo

Image capture with optional caption.

  • Camera or gallery source
  • Optional cropping
  • Date-organized storage

Handwriting

Canvas-based handwriting/drawing.

  • Lined paper background
  • PNG export
  • Touch/stylus input

Para-ID System

Portable identifiers for cross-device synchronization:

# Journal file format: Daily/journals/{YYYY-MM-DD}.md

---
entries:
  daily:abc123def456:
    type: voice
    audio: assets/2025-01-28/audio_abc123.wav
    duration: 45
    created: "10:30"
    status: complete
---

# para:daily:abc123def456 Morning reflection

This is the transcribed text from the voice recording.
It was captured at 10:30 AM and processed through
the streaming transcription pipeline.

---

# para:daily:xyz789uvw012 Follow-up thought

Another entry with its own para ID and metadata.

ID Format

Component Value Description
Prefix para: Namespace identifier
Module daily: Source module (daily, chat)
ID 12 chars Alphanumeric (a-z, 0-9)
Legacy ID 6 chars Backward compatible

Omi Wearable Integration

Bluetooth Low Energy (BLE) integration with Omi pendant device:

┌─────────────────────────────────────────────────────────────────────┐
│                      OMI DEVICE INTEGRATION                          │
│                                                                      │
│  ┌───────────────┐         ┌───────────────────────────────────┐   │
│  │  Omi Pendant  │  ◀─BLE─▶│       OmiBluetoothService         │   │
│  │               │         │                                    │   │
│  │  • Button     │         │  • Scan for devices               │   │
│  │  • Microphone │         │  • Auto-reconnect                 │   │
│  │  • SD Card    │         │  • Battery level stream           │   │
│  │  • Battery    │         │  • Connection state               │   │
│  └───────────────┘         └───────────────┬───────────────────┘   │
│                                             │                       │
│                                             ▼                       │
│                            ┌───────────────────────────────────┐   │
│                            │       OmiCaptureService           │   │
│                            │                                    │   │
│                            │  Mode Auto-Detection:             │   │
│                            │  ┌──────────────────────────────┐ │   │
│                            │  │ Store-and-Forward (default)  │ │   │
│                            │  │ Records to SD card           │ │   │
│                            │  │ Downloads when complete      │ │   │
│                            │  └──────────────────────────────┘ │   │
│                            │  ┌──────────────────────────────┐ │   │
│                            │  │ Real-time Streaming (fallback)│ │   │
│                            │  │ Audio over BLE during record │ │   │
│                            │  └──────────────────────────────┘ │   │
│                            └───────────────────────────────────┘   │
│                                                                      │
│  Supported Codecs: PCM8, PCM16, mu-law, Opus                        │
│  Service UUID: 19b10000-e8f2-537e-4f6c-d104768a1214                 │
└─────────────────────────────────────────────────────────────────────┘

Button Events

  • Single Tap: Start/stop recording
  • Double Tap: Cancel recording
  • Triple Tap: Custom action

Core Services

JournalService (1,261 lines)

Core CRUD operations for journal entries with surgical file appending.

Method Purpose
loadDay(date) Load all entries for a date
addEntry(entry) Append new entry (preserves external edits)
updateEntry(paraId, content) Surgical update of specific entry
deleteEntry(paraId) Remove entry and metadata
listRecordingIds() Get all para IDs with recordings

LiveTranscriptionService (547 lines)

Main transcription orchestrator with VAD-based auto-pause.

// Public API
bool get isRecording
bool get isProcessing
List<TranscriptionSegment> get segments
String get interimText
StreamingTranscriptionState get currentStreamingState

Stream<bool> get vadActivityStream
Stream<AudioDebugMetrics> get debugMetricsStream
Stream<StreamingTranscriptionState> get streamingStateStream

Future<bool> initialize()
Future<bool> startRecording()
Future<String?> stopRecording()  // Returns audio path
Future<void> cancelRecording()

AgentOutputService (171 lines)

Loads agent-generated outputs (reflections, content ideas).

  • Parses markdown with YAML frontmatter
  • Extracts agent name and timestamp
  • Lists outputs by date or agent

Crash Recovery

Segments are persisted before transcription for recovery:

// pending_segments.json
{
  "segments": [
    {
      "index": 0,
      "audioFilePath": "/tmp/recording_abc123.wav",
      "startOffsetBytes": 44,
      "durationSamples": 160000,
      "status": "interrupted",
      "transcribedText": null,
      "createdAt": "2025-01-28T10:30:00Z"
    }
  ]
}

Recovery Strategy

  • Segments marked pending, processing, or interrupted are recovered
  • Audio files retained for 7 days
  • On restart, re-queues interrupted segments

Daily Agent Integration

Server-side agents process journal entries to generate reflections:

Journal Entry
     │
     ├── Written to Daily/journals/{date}.md
     │
     ▼
Server Scheduler (APScheduler)
     │
     ├── Discovers agents from Daily/.agents/*.md
     │
     ├── Morning Reflection Agent (configurable time)
     │   └── Reads journal + chat logs
     │   └── Generates morning reflection
     │   └── Writes to Daily/reflections/{date}.md
     │
     └── Content Scout Agent (configurable time)
         └── Extracts valuable insights
         └── Writes to Daily/content-scout/{date}.md

See Modules & Agents documentation for server-side details.

File Storage

~/Parachute/Daily/
├── journals/
│   ├── 2025-01-28.md    # Journal entries with YAML frontmatter
│   └── 2025-01-27.md
├── assets/
│   └── 2025-01-28/      # Date-organized media
│       ├── audio_abc123.wav
│       ├── photo_xyz789.jpg
│       └── handwriting_uvw012.png
├── reflections/
│   ├── 2025-01-28.md    # AI-generated morning reflection
│   └── 2025-01-27.md
├── chat-log/
│   └── 2025-01-28.md    # Chat session summaries
├── content-scout/
│   └── 2025-01-28.md    # Content ideas extracted
└── .agents/
    ├── curator.md       # Daily curator agent config
    └── content-scout.md # Content scout agent config

Key Widgets

Widget Lines Purpose
journal_screen.dart 2,156 Main journal UI - timeline of entries
journal_input_bar.dart 1,379 Text input + voice recording controls
journal_entry_row.dart 992 Row item in list view
journal_entry_card.dart 606 Card displaying single entry
entry_edit_modal.dart 572 Modal for editing entry
agent_output_header.dart 511 Header showing agent output
mini_audio_player.dart 260 Audio playback widget

Related Documentation