# Architecture

## Overview

Thulur Frontend is a Compose Multiplatform desktop application structured around a shared core in `commonMain` and desktop adapters in `jvmMain`. Although the repository uses Kotlin Multiplatform, the active runtime today is the JVM desktop app.

The codebase follows a practical layered design:

- `thulur_api` handles backend transport details
- `data` maps DTOs and supplies session/cache implementations
- `domain` defines models, interfaces, and thin use cases
- `presentation` owns ViewModels, screens, routing, and shared UI primitives
- `jvmMain` bridges desktop-only capabilities into the shared app

## High-level runtime layout

```mermaid
flowchart LR
    A["Desktop entry: main.kt"] --> B["App.kt"]
    B --> C["Koin modules"]
    C --> D["AppRootScreenModel"]
    D --> E["AppRouter / Voyager screens"]
    E --> F["Feature ViewModels"]
    F --> G["Domain use cases"]
    G --> H["RemoteThulurApiRepository"]
    H --> I["ThulurApi facade"]
    I --> J["Ktor HTTP methods"]
    J --> K["https://api.thulur.com"]

    C --> L["JVM adapters"]
    L --> M["Secure token store"]
    L --> N["Theme store"]
    L --> O["Browser passkey auth"]
    L --> P["JCEF article runtime"]
```

## Source set responsibilities

| Source set | Responsibility |
| --- | --- |
| `composeApp/src/commonMain` | Shared app logic, domain models, ViewModels, routing, theme, and backend transport abstractions |
| `composeApp/src/jvmMain` | Desktop entry point and JVM-specific integrations |
| `composeApp/src/commonTest` | Platform-neutral tests for transport, repositories, use cases, and helpers |
| `composeApp/src/jvmTest` | JVM tests for ViewModels, route keys, desktop auth, secure storage, and JCEF bridge behavior |

## Startup sequence

### 1. Desktop process boot

The desktop app starts in `composeApp/src/jvmMain/kotlin/com/example/thulur/main.kt`.

Important behavior:

- prewarms the JCEF runtime before the UI is used
- opens a single Compose `Window`
- centers the window and applies a minimum size
- shuts JCEF down on window close

### 2. App composition root

`composeApp/src/commonMain/kotlin/com/example/thulur/App.kt`:

- installs a singleton Coil image loader
- starts Koin with `appModules`
- wraps the app in `ProvideThulurDesignScale`
- renders `AppRootRoute()`

### 3. Dependency graph

`composeApp/src/commonMain/kotlin/com/example/thulur/di/AppModules.kt` defines four Koin modules:

- `apiModule`
- `dataModule`
- `domainModule`
- `presentationModule`

This is the main composition root for the application.

## Layer responsibilities

## `thulur_api`

Package: `composeApp/src/commonMain/kotlin/com/example/thulur_api`

Responsibilities:

- construct a Ktor client
- define a single `ThulurApi` facade
- isolate each backend capability in a dedicated method class
- define DTOs that match backend payloads

Notable files:

- `ThulurApi.kt`
- `client/ThulurHttpClientFactory.kt`
- `config/ThulurApiConfig.kt`
- `methods/*`
- `dtos/*`

## `data`

Package: `composeApp/src/commonMain/kotlin/com/example/thulur/data`

Responsibilities:

- map DTOs to domain models
- provide concrete session and cache implementations
- hold the main backend repository implementation

The central integration point is:

- `data/repository/RemoteThulurApiRepository.kt`

## `domain`

Package: `composeApp/src/commonMain/kotlin/com/example/thulur/domain`

Responsibilities:

- define models used by the UI
- expose stable interfaces for session, theme, auth, and repository access
- keep task-level use cases thin and composable

This layer intentionally stays light. Most orchestration happens in ViewModels.

## `presentation`

Package: `composeApp/src/commonMain/kotlin/com/example/thulur/presentation`

Responsibilities:

- screen state and user interaction logic
- navigation and route key construction
- shared UI components and design system wrappers
- feature-specific rendering logic

Feature areas:

- `auth`
- `dailyfeed`
- `dailyfeed/article_reader`
- `dailyfeed/thread_history`
- `chat`
- `settings`
- `root`
- `router`
- `theme`
- `composables`

## Navigation model

Routing lives in `presentation/router/AppRouter.kt` and uses Voyager `Screen` objects.

The root navigation decision is derived from `AppRootUiState`:

- `Loading` -> loading screen
- `Unready` -> auth flow
- `Ready` -> daily feed flow

Primary screens:

- `AuthScreen`
- `DailyFeedScreen`
- `SettingsScreen`
- `ThreadHistoryScreen`
- `ChatScreen`
- `ArticleReaderScreen`

Route keys are centralized in `presentation/router/RouterScreenKeys.kt` and verified in JVM tests.

## Root bootstrap model

`presentation/root/AppRootScreenModel.kt` is the app-level coordinator. It:

- loads any persisted token on startup
- listens to the current session flow
- resolves the authenticated or unauthenticated app shell
- boots theme mode from local storage, and falls back to backend settings
- loads user settings needed by the global shell
- loads subscription tier with retries

This is the main place where app-wide session transitions are normalized.

## Session and persistence model

The shared session contract is `domain/session/CurrentSessionProvider.kt`.

The current implementation:

- stores the active bearer token in memory
- exposes it via `sessionFlow`
- loads a persisted token once on startup
- clears token and read cache on logout

Related components:

- `CurrentSessionProviderImpl`
- `SecureTokenStore`
- `JvmSecureTokenStore`
- `InMemorySecureTokenStore`
- `ReadArticlesCache`
- `InMemoryReadArticlesCache`

Important behavior:

- secure token persistence is best-effort
- if OS-backed credential storage is unavailable, the app falls back to an in-memory token store
- read article state is cached in memory and cleared when the session is cleared

## Platform-specific integrations

## Browser passkey authentication

Desktop auth lives in:

- `jvmMain/data/auth/BrowserPasskeyAuthenticator.jvm.kt`

The flow:

1. Start backend auth via `/auth/desktop/start`
2. Open the returned browser URL in the system browser
3. Listen on a local callback server at `127.0.0.1`
4. Validate callback `state`
5. Exchange the callback code via `/auth/exchange`
6. Store the returned bearer token in the shared session provider

## Secure token persistence

Desktop secure token storage lives in:

- `jvmMain/data/session/JvmSecureTokenStore.kt`

It uses Microsoft `credential-secure-storage` and only keeps the OS-backed implementation if it is actually secure.

## Theme persistence

Desktop theme persistence lives in:

- `jvmMain/data/theme/JvmThemeStore.kt`

It stores dark-mode preference using `java.util.prefs.Preferences`.

## Embedded article reader

Desktop article rendering uses JCEF:

- runtime bootstrap in `jvmMain/presentation/dailyfeed/article_reader/JcefBrowserRuntime.jvm.kt`
- desktop web view implementation in `jvmMain/presentation/dailyfeed/article_reader/PlatformArticleWebView.jvm.kt`

The runtime is prewarmed on application startup and disposed during shutdown.

## Theme system

Shared theme code lives in `presentation/theme`.

Important concepts:

- `ThulurTheme`
- `ThemeMode`
- semantic color and typography wrappers
- a shared design scale provider

The root router reads the current app theme and wraps the current screen in `ThulurTheme(mode = ...)`.

## Architectural conventions

- Keep backend request knowledge inside `thulur_api/methods`.
- Keep DTO-to-domain mapping in `RemoteThulurApiRepository`.
- Use domain use cases as thin adapters, not heavy service layers.
- Put feature orchestration in ViewModels.
- Keep desktop-only behavior out of shared feature logic unless it is abstracted behind an interface or `expect/actual` boundary.

## Key files to open first

- `composeApp/src/jvmMain/kotlin/com/example/thulur/main.kt`
- `composeApp/src/commonMain/kotlin/com/example/thulur/App.kt`
- `composeApp/src/commonMain/kotlin/com/example/thulur/di/AppModules.kt`
- `composeApp/src/commonMain/kotlin/com/example/thulur/presentation/root/AppRootScreenModel.kt`
- `composeApp/src/commonMain/kotlin/com/example/thulur/presentation/router/AppRouter.kt`
- `composeApp/src/commonMain/kotlin/com/example/thulur/data/repository/RemoteThulurApiRepository.kt`
- `composeApp/src/commonMain/kotlin/com/example/thulur_api/ThulurApi.kt`
