* plan: UI improvements to improve LLM usage via SimpleX * core, ui: types for chat layout * core, ui: settings for alternative terminal-like chat layout for managing LLMs or other service bots * plan: message priority * fixes * query plan * update plans * remove silent messages plan * rename migrations * implement layout * amend * fix * fix * fix * improve * time on the right, sender name in blue * no avatar in direct chats * better previews * update, fixes * update * time gap * changes * ui: terminal layout line heights, gaps and time placement * ui: simplify terminal meta layout, correct its docs and tests * ui: right-to-left emoji edge in terminal rows, time placement tests * android, desktop: raise Kotlin compiler memory to 4 GB * ui: terminal captions as follow-up text, classic event separators * ui: terminal media edge and overlay time, plan decisions * ui: simplify terminal row separation, correct doc line references * ui: keep the media time inside terminal rows, restore caption conditions * ui: terminal heading capitals at large font scales, idle live message time * ui: rename the classic voice-with-text flag, correct the meta copy docs * ui: smaller terminal layout diff, sender gap and meta names * ui: terminal row as tall as the media time * docs: remove the chat layout section from the bots readme * docs: shorten the terminal layout section of the chat view * core: address settings API as on master, /auto_accept reads the stored layout * ui: smaller terminal headings * ui: classic sender rows and avatar rule as on master * ui: classic reply quote text colour as on master * ios: keep the chat layout setting with other UI settings * ui: go-to button at the end of terminal rows, no gutter in direct chats * ui: forwarded arrow on the header line of terminal rows, above the time * ui: forwarded arrow centred above the time line of terminal rows * ios: terminal chat layout, chosen per app, profile and chat * ios: event time reserve of three spaces, app layout picker as Kotlin * ios: spec and plan of the terminal chat layout * ios: classic message text at 87% in dark themes, as terminal rows * ios: terminal gaps scaled by the body size, 17/16 of Kotlin values * ios: terminal quote sender at 13.5pt, thumbnail at the item end * ios: product docs of the terminal layout, plans updated for iOS * ios: chat scenes rendered to PNG by a unit test in the simulator CI * ios: snapshots rendered by the smoke test script, workflow unchanged * ios: terminal names at 13.5 x 17/16 pt, as the gaps * ios: Markdown views at the 87% message colour in dark themes * ios: terminal quote thumbnail 12pt after the quote text or the bar * ios: terminal rule-only message widened by a wider quote or header * ios: member avatars scaled with the text size, as in the chat list * ios: snapshot rows at their own height, as list cells, so text wraps * ios: member avatars back at 34pt, for the snapshot before scaling * ios: member avatars scaled with the text size, as in the chat list * ui: terminal quote thumbnail 12dp after the quote text or the bar * ios: classic group bubble width follows the scaled member avatar * ci: ios-sim README describes the snapshots rendered after the smoke test * ios: scratch, padding variants for the I7 and I8 renders, to revert * ios: revert the scratch padding variants after their renders * ui: contrast of the voice message button in terminal rows * ios: scratch, spacing variants and list margins to render, to revert * ios: revert the scratch spacing variants after their renders * ios: snapshots at a fixed date, in UTC and the en_US locale * ios: snapshots drawn as 8-bit sRGB without alpha * ci: compare iOS snapshots with the references in the repository * ci: record and check iOS snapshot references on GitHub's runner * ios: scratch, list margins on the remaining screens, to revert * ios: revert the scratch list margins after their measurement * ci: snapshot scripts executable, as the other CI scripts * ios: scratch, voice circle variants in light themes, to revert * ios: revert the scratch voice circle variants * ios: one 12pt padding at the edges and after avatars in terminal rows * ios: classic group sender name on the bubble edge * ui: one 12dp padding at the edges and after avatars in terminal rows * ios: one heading scale in both chat layouts * ios: snapshot references of the chat scenes * docs: leave the headings and classic lines of the plan to ep/classic-bubbles * ios: declare terminal layout functions inside types * ios: keep layout constants at file scope * ios: keep file-scope functions, move only extension members into types * ios: snapshot tests record row heights at first layout and the appearance layout switch * ios: appearance preview follows the chat layout setting at once * ios: snapshot test measures first-layout rows by SwiftUI and by cells * ios: chat rows measured at the chat width when they first appear * ios: snapshot test checks that chat rows keep their first height after a reload * ios, ci: references of terminal and classic chat scenes, dark message text * ios: message text at 95% in dark themes * ios, ci: references with message text at 95% in dark themes * ios: media in terminal rows end at the time's right inset * ios, ci: references with terminal media at the time's right inset --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
SimpleX Chat -- Kotlin Multiplatform Specification
Table of Contents
Executive Summary
SimpleX Chat is a Kotlin Multiplatform application targeting Android and Desktop (JVM) platforms. The UI layer is built entirely with Jetpack Compose. The application communicates with a Haskell-based cryptographic core (simplex-chat) through a JNI bridge -- native functions declared in Kotlin and linked at runtime to a shared library (libapp-lib). Platform-specific behavior (notifications, file system paths, services, audio/video) is abstracted using the expect/actual pattern and a runtime-assignable PlatformInterface callback object.
The Gradle project is structured as three modules:
| Module | Purpose |
|---|---|
:common |
Shared Compose UI, models, platform abstractions (commonMain, androidMain, desktopMain) |
:android |
Android application entry point (SimplexApp, MainActivity) |
:desktop |
Desktop application entry point (Main.kt, showApp()) |
All meaningful application logic resides in :common/commonMain. Platform source sets (androidMain, desktopMain) provide actual implementations for expect declarations and host platform-specific integration code.
Dependency Graph
App Entry Points
+-- Android: SimplexApp.onCreate -> initHaskell -> initMultiplatform -> initChatControllerOnStart
| MainActivity.onCreate -> setContent { AppScreen() }
+-- Desktop: main() -> initHaskell -> runMigrations -> initApp -> showApp -> AppWindow -> AppScreen()
|
v
Common Module (commonMain)
+-- ChatModel (Compose state singleton) <-> ChatController/SimpleXAPI (JNI bridge) <-> Haskell Core (chat_ctrl)
+-- Views (Compose)
| +-- App.kt: AppScreen -> MainScreen
| +-- ChatListView -> ChatView -> ComposeView -> SendMsgView
| +-- ChatItemView (message rendering: text, image, video, voice, file, call, events)
| +-- Settings: SettingsView, UserProfileView, UserProfilesView
| +-- Onboarding: OnboardingView, WhatsNewView, CreateFirstProfile
| +-- Call: CallView, IncomingCallAlertView
| +-- Database: DatabaseView, DatabaseEncryptionView, DatabaseErrorView
| +-- Groups: GroupChatInfoView, AddGroupMembersView, GroupMemberInfoView
| +-- Contacts: ContactListNavView
| +-- Remote: ConnectDesktopView, ConnectMobileView
| +-- Terminal: TerminalView
+-- Models
| +-- ChatModel -- global app state (Compose MutableState singleton)
| +-- ChatsContext -- per-context chat list state (primary + optional secondary)
| +-- Chat -- per-conversation state (chatInfo, chatItems, chatStats)
| +-- ChatController -- API command dispatch, event receiver, preferences
| +-- AppPreferences -- 150+ SharedPreferences keys
+-- Services
| +-- NtfManager -- abstract notification coordinator (Android/Desktop implementations)
| +-- SimplexService -- Android foreground service for background messaging
| +-- ThemeManager -- theme resolution (system/light/dark/simplex/black + per-user overrides)
| +-- CallManager -- WebRTC call lifecycle
+-- Platform (expect/actual)
+-- Core.kt -- JNI declarations (external fun), initChatController, chatInitTemporaryDatabase
+-- AppCommon.kt -- runMigrations, AppPlatform enum
+-- Files.kt -- dataDir, tmpDir, filesDir, dbAbsolutePrefixPath (expect)
+-- Share.kt -- shareText, shareFile, openFile (expect)
+-- VideoPlayer.kt -- VideoPlayerInterface, VideoPlayer (expect class)
+-- RecAndPlay.kt -- RecorderInterface, AudioPlayerInterface (expect)
+-- UI.kt -- showToast, hideKeyboard, getKeyboardState (expect)
+-- Notifications.kt -- allowedToShowNotification (expect)
+-- NtfManager.kt -- abstract NtfManager class
+-- Platform.kt -- PlatformInterface (runtime callback object)
+-- Cryptor.kt -- CryptorInterface (expect)
+-- Images.kt -- bitmap utilities (expect)
+-- SimplexService.kt-- getWakeLock (expect)
+-- Log.kt, Modifier.kt, Back.kt, ScrollableColumn.kt, PlatformTextField.kt, Resources.kt
Specification Documents
| Document | Path | Description |
|---|---|---|
| Architecture | spec/architecture.md | System layers, module structure, JNI bridge, app lifecycle, event streaming, platform abstraction |
| State Management | spec/state.md | ChatModel singleton, ChatsContext, Chat data class, AppPreferences, ActiveChatState |
| API | spec/api.md | ChatController command dispatch, ~150 API functions in 11 categories, CC/CR/API types |
| Database | spec/database.md | SQLite database files, migrations, encryption, backup/restore |
| Impact | spec/impact.md | Source file → product concept mapping for change impact analysis |
| Chat View | spec/client/chat-view.md | ChatView, ChatItemView, message rendering, item interactions, terminal layout (TerminalItemView) |
| Chat List | spec/client/chat-list.md | ChatListView, ChatPreviewView, filtering, search, tags |
| Compose | spec/client/compose.md | ComposeView, SendMsgView, ComposeState, attachments, mentions |
| Navigation | spec/client/navigation.md | App screen routing, onboarding, settings, new chat flows |
| Calls | spec/services/calls.md | WebRTC call lifecycle, signaling, platform-specific call views |
| Files | spec/services/files.md | File transfer (SMP inline / XFTP), CryptoFile encryption, platform file paths |
| Notifications | spec/services/notifications.md | NtfManager, SimplexService, notification channels, background delivery |
| Theme | spec/services/theme.md | ThemeManager, color system, wallpapers, per-user overrides |
Product Documents
| Category | Path | Topic |
|---|---|---|
| Overview | product/README.md | Product overview, capability map, navigation map |
| Concepts | product/concepts.md | 32 product concepts (PC1-PC32) mapped to docs + source |
| Glossary | product/glossary.md | Domain term definitions (9 sections) |
| Rules | product/rules.md | 18 business rules in 6 categories |
| Gaps | product/gaps.md | 8 known gaps with recommendations |
| Flows | product/flows/ | onboarding, messaging, connection, calling, file-transfer, group-lifecycle |
| Views | product/views/ | chat-list, chat, settings, onboarding, call, new-chat, contact-info, group-info, user-profiles |
Source Entry Points
| Component | File | Key Symbol | Line |
|---|---|---|---|
| Android Application | SimplexApp.kt |
class SimplexApp |
41 |
| Android Activity | MainActivity.kt |
class MainActivity |
27 |
| Desktop Entry | Main.kt |
fun main(args: Array<String>) |
26 |
| Desktop App Window | DesktopApp.kt |
fun showApp() |
33 |
| Desktop Init | AppCommon.desktop.kt |
fun initApp() |
21 |
| Common App Screen | App.kt |
fun AppScreen() |
47 |
| JNI Bridge | Core.kt |
external fun initHS() |
18 |
| Chat Controller | SimpleXAPI.kt |
object ChatController |
525 |
| Chat Model | ChatModel.kt |
object ChatModel |
86 |
| App Preferences | SimpleXAPI.kt |
class AppPreferences |
102 |
| Platform Interface | Platform.kt |
interface PlatformInterface |
15 |
| Notification Manager | NtfManager.kt |
abstract class NtfManager |
20 |
| Theme Manager | ThemeManager.kt |
object ThemeManager |
18 |
| Android Haskell Init | AppCommon.android.kt |
fun initHaskell(packageName: String) |
36 |
| Common Migrations | AppCommon.kt |
fun runMigrations() |
48 |
| Android Service | SimplexService.kt |
class SimplexService |
41 |
| Gradle Root | settings.gradle.kts |
include(":android", ":desktop", ":common") |
22 |
| Common Build | build.gradle.kts |
kotlin { androidTarget(); jvm("desktop") } |
14 |