Phase 1 of docs/chapter-uploads.md. The picker and the decode, with one file going into the add-chapter form that already works -- so the platform seam is exercised on its own, before anything knows about several chapters at once. Three actuals rather than FileKit. This is the app's only dialog, the calls are a dozen lines each, and the project already keeps four platform seams of this shape; the library's Compose-version coupling buys nothing here, and the ios target cannot be compiled on a linux host either way. ChapterText.decode is the part that matters. The web app does `file.data.toString()`, where a .docx becomes a chapter of zip-header garbage the uploader can go back and delete; here the next thing that happens to the text is that a quorum signs it. So: strict UTF-8, because a lenient decode hides U+FFFD inside something permanent; a NUL sniff before decoding, because mostly-ASCII binary decodes cleanly; the byte-order mark stripped and CRLF normalised, because both change the bytes without changing a word and the same prose from two machines has to give the same chunk ids. The pickers stop one byte past the cap rather than at it, so an over-size file reads as over-size instead of as a chapter with its ending missing. Android registers both open contracts so asking for one file does not offer a picker that returns six. The dialog title is a parameter read from the catalogue, since stringResource is composable and a FileDialog is not. ChapterNaming came out of the two answers the web app gives on its two routes -- the filename on the upload, the first chunk on the paste form -- both of which are right some of the time. The filename minus extension and ordinal prefix, falling through to a heading and then the first line, because `01.md` is not a chapter title and `03 - The Wreck.md` is. FilePicker.ios.kt is unverified: the ios targets are declared only on macos, so it is not compiled here. Written to the shape of the eight .ios.kt actuals beside it and says so in its KDoc. A stub returning no files would read as a working picker that a reader's chapters vanish into. 22 new tests, all pure; m3Audit's budgets all met. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).
-
/composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:
- commonMain is for code that’s common for all targets.
- Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
-
/iosApp contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.
Build and Run Android Application
To build and run the development version of the Android app, use the run configuration from the run widget in your IDE’s toolbar or build it directly from the terminal:
- on macOS/Linux
./gradlew :composeApp:assembleDebug - on Windows
.\gradlew.bat :composeApp:assembleDebug
Build and Run Desktop (JVM) Application
To build and run the development version of the desktop app, use the run configuration from the run widget in your IDE’s toolbar or run it directly from the terminal:
- on macOS/Linux
./gradlew :composeApp:run - on Windows
.\gradlew.bat :composeApp:run
Build and Run iOS Application
To build and run the development version of the iOS app, use the run configuration from the run widget in your IDE’s toolbar or open the /iosApp directory in Xcode and run it from there.
Learn more about Kotlin Multiplatform…