Architecture
Daylight is built with a local-first architecture that prioritizes data ownership, offline capability, and cross-platform support.
System overview
Section titled “System overview”┌─────────────────────────────────────────────────────┐│ UI Layer ││ Svelte 5 + Runes │├─────────────────────────────────────────────────────┤│ Bridge Layer ││ Tauri 2 Commands │├─────────────────────────────────────────────────────┤│ Core Layer ││ Rust + Filesystem │├─────────────────────────────────────────────────────┤│ Storage Layer ││ Markdown + YAML Files │└─────────────────────────────────────────────────────┘Technology stack
Section titled “Technology stack”Frontend
Section titled “Frontend”| Technology | Purpose |
|---|---|
| Svelte 5 | Reactive UI framework with fine-grained updates |
| Runes | Svelte’s new reactivity primitives ($state, $derived, $effect) |
| Tailwind CSS | Utility-first styling with theme support |
| TypeScript | Type safety throughout the codebase |
Backend
Section titled “Backend”| Technology | Purpose |
|---|---|
| Tauri 2 | Native app shell with minimal footprint |
| Rust | System-level file operations and parsing |
| Bun | JavaScript runtime and package manager |
Storage
Section titled “Storage”| Technology | Purpose |
|---|---|
| Markdown | Human-readable task content (notes, descriptions) |
| YAML | Structured metadata in frontmatter |
| Filesystem | Direct file access—no database layer |
UI layer
Section titled “UI layer”Svelte 5 runes
Section titled “Svelte 5 runes”Daylight uses Svelte 5’s runes for reactive state management:
// Reactive state - tasks stored in a Maplet taskFiles = $state(new Map());
// Derived computations - filtered viewslet activeTasks = $derived( [...taskFiles.values()].filter(t => t.status === 'active'));
// Effects - side effects on state changes$effect(() => { console.log(`${activeTasks.length} active tasks`);});[!note] Map reactivity in Svelte 5 requires creating a new Map on mutation:
// This triggers reactivitytaskFiles = new Map(taskFiles).set(filename, newData);
Key components
Section titled “Key components”| Component | Purpose |
|---|---|
ViewTaskRow | Task row display with actions |
TaskEditModal | Full task editor (notes, tags, projects, recurrence) |
TaskContextMenu | Quick actions (reschedule, delete, edit) |
RecurrenceEditor | UI for setting up recurring tasks |
View services
Section titled “View services”The ViewService transforms raw task data into grouped views:
- Past: Scheduled before today, still active
- Now: Scheduled today or no date
- Upcoming: Scheduled for future dates
- Wrapped: Completed today
The RecurringInstanceService expands recurring task series into individual instance rows.
Bridge layer
Section titled “Bridge layer”Tauri commands
Section titled “Tauri commands”Tauri provides a secure bridge between the web frontend and Rust backend:
#[tauri::command]async fn read_tasks(path: String) -> Result<Vec<Task>, Error> { // Read .md files from directory // Parse YAML frontmatter // Return task data to frontend}
#[tauri::command]async fn write_task(path: String, task: Task) -> Result<(), Error> { // Serialize task to YAML + Markdown // Write to filesystem}Capabilities and permissions
Section titled “Capabilities and permissions”Tauri 2 uses a capability system for security. Daylight’s permissions include:
- fs:scope — Access to configured task directories
- path:all — Path resolution for cross-platform compatibility
- dialog:open — Directory picker for data path selection
On Android, additional permissions are required:
MANAGE_EXTERNAL_STORAGE— Access files outside app sandbox (for Syncthing folders)
Storage layer
Section titled “Storage layer”File format
Section titled “File format”Each task is a single Markdown file:
---YAML frontmatter (metadata)---
Markdown content (notes)This format is:
- Human-readable — Open in any text editor
- Version-control friendly — Works with git
- Parser-friendly — Standard YAML + Markdown
Data flow
Section titled “Data flow”Reading tasks:
App startup │ ▼Tauri reads task directory │ ▼Rust parses each .md file │ ▼Tasks sent to Svelte via IPC │ ▼Store updates, UI rendersWriting tasks:
User edits task │ ▼Svelte dispatches save │ ▼Tauri command invoked │ ▼Rust serializes to YAML + Markdown │ ▼File written to disk │ ▼Syncthing picks up change (if configured)Platform support
Section titled “Platform support”| Platform | Status | Notes |
|---|---|---|
| Linux | Supported | Primary development platform; AppImage distribution |
| Android | Supported | APK distribution; requires storage permissions |
| Windows | Planned | Tauri supports Windows; needs testing |
| macOS | Planned | Tauri supports macOS; needs testing |
Android considerations
Section titled “Android considerations”- Uses custom Kotlin plugins for storage permissions
- Default path assumes Syncthing at
/storage/emulated/0/syncthing/ - Data path override stored in
localStoragefor persistence
Why this architecture?
Section titled “Why this architecture?”Local-first benefits
Section titled “Local-first benefits”- Offline by default — No network required for any operation
- Data ownership — Files live on your device, not a server
- Sync flexibility — Use any file sync tool (Syncthing, Dropbox, etc.)
- Longevity — Plain text files outlive any app
Tradeoffs
Section titled “Tradeoffs”- No real-time collaboration — File sync has inherent latency
- Manual conflict resolution — When sync conflicts occur
- Scale limits — File-per-task doesn’t scale to millions of tasks
Related
Section titled “Related”- Task File Format — YAML frontmatter specification
- Sync & Conflicts — Multi-device setup
- Limitations — What Daylight doesn’t do