Skip to content

Repository files navigation

DuskRead

DuskRead

A reading list that keeps itself, ranked by what you actually read.
An AI agent checks your inbox and files what's worth keeping into Notion;
blogs you follow add their own posts the same way.

Curated through Notion Kotlin and Compose Multiplatform SwiftUI on iOS Android 12 and up MIT License

See every screen  ·  How it fits together  ·  Run it

Monochrome by default. One accent, spent on purpose. Under 4 MB, after R8.


Newsletters and blogs file themselves into one Notion database — ranked by what you actually read, not by when it arrived — then you set a twenty-five minute timer and read, on Paper Black or Ink, the same page with the colour drained out.

What it does

Three sources — Gmail, RSS feeds, and links you paste or share. Claude files the mail into Notion's Sources and Reading List, which syncs both ways with DuskRead; feeds and shared links reach the app directly, never touching Notion. The app caches everything, reads offline, and reads articles aloud on the phone

  • Curated through Notion. Claude files inbox newsletters straight into the reading list; blogs with a working feed are fetched by the app and never touch Notion. Either way, only rows ticked Saved reach the phone — see docs/architecture.md for the schema and sync rules. Following a blog writes it back to Notion too, so it's there to edit next time you open it.
  • Save it however it reaches you — the Chrome share sheet, or the paste field on Saved behind Add.
  • Reads offline. Every screen renders from local storage; a feed with full-content RSS is cached whole at sync time, so most articles were already on the phone before you tapped them.
  • Saved is a queue and a record. Unread leads; what you've read stays under its own heading. Filter to either, or search a title, host or topic.
  • On-device summaries and reading aloud. Long articles get a one-tap summary via Gemini Nano; the phone can also read one aloud with its own text-to-speech. Both are absent on hardware that can't run them, and a read that can't happen says why instead of staying silent.
  • A focus timer that behaves like an alarm, not a widget — a real system notification and vibration when the interval ends, phone face-down.

Getting started

Android is the app, built and tested day to day. iOS draws its own UI in SwiftUI over the same Kotlin.

git clone https://github.com/MKS-01/duskread.git && cd duskread
./gradlew :androidApp:installDebug      # ~5 s once warm

That's the whole setup — Notion is optional, and connecting it is one pasted token in Settings.

JDK 17 or newer
Android 12 and up (minSdk 31, compile/target 36)
Gradle 9.3.1, via the wrapper
For summaries A phone with AICore — Pixel 9+, Galaxy S24+ and similar. No emulator has it
For iOS Xcode and XcodeGen; iOS 18 and up
iOS
cd iosApp && xcodegen generate && cd ..             # needs an Xcode host
open iosApp/iosApp.xcodeproj

There's no Gradle run task for it — Compose Multiplatform only builds a framework, which the generated Xcode project links. First build is ten minutes or more; incremental after that.

One core, two UIs

One commonMain source set holds the libraries, the ranking, the sync and the Compose UI itself, shared between Android and iOS; storage, audio, the summariser, the focus timer and the HTTP client are expect/actual pairs behind it, on Kotlin 2.3 and Ktor 3.1. Android draws that Compose UI directly with Compose Multiplatform 1.11 and Haze for the floating-bar blur; iOS is the one platform that composes its own, in SwiftUI over iosMain/bridge/, taking its colours, type, spacing, motion and icon paths from the same Kotlin token layer rather than a second copy of them. Summaries run on-device via ML Kit GenAI / Gemini Nano; reading aloud is Android's own TextToSpeech, and AVSpeechSynthesizer on iOS through the same shared Flow<SpeechProgress> contract; the optional Readback tab queries readback's library.db read-only, Android-only for now. No navigation library, no ViewModel, no DI, no database — a ceiling chosen on purpose.

commonMain holds the business logic (data, links, notion, pomodoro, reader, speech, summary), the Compose UI itself, and the design tokens. Android draws that Compose UI directly through platform actuals. iOS goes through iosMain/bridge/, which wraps AppGraph in an observable, Obj-C-safe layer, and iosApp draws its own SwiftUI from the same tokens

./gradlew ktlintCheck    # several rules deliberately off, see .editorconfig
./gradlew ktlintFormat

No tests yet — a build that succeeds is the start of checking a change, not the end of it.

The design system — Paper Black and Ink, and every token behind them. docs/architecture.md — how the pieces connect, the Notion schema, and the end-to-end flows.

Licence

MIT — see LICENSE.

A personal project, built for learning and for its own author to use
Built agent-first with Claude Code
The synced-audio Readback tab is a separate, optional open-source project: readback

About

Kotlin Multiplatform read-it-later app for Android and iOS — RSS feeds, offline reading, on-device summaries and text-to-speech, and a focus timer, with optional Notion sync. Compose UI on Android, SwiftUI on iOS.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages