Files
vnidrop/shared/AGENTS.md
Hammed Abass 6ab658fea2 feat(shared): graduate saved-device transfer experience
Add the VniDrop-specific Compose architecture skill, unify invitation and targeted transfer drafts, and promote Saved devices to an adaptive first-class destination.
2026-08-12 21:45:18 +02:00

134 lines
4.8 KiB
Markdown

# AGENTS.md — `shared/` (Compose Multiplatform + KMP)
Nearest guide when editing under `shared/`. Root [`AGENTS.md`](../AGENTS.md)
still applies; this file wins for UI/KMP work.
---
## Purpose
`shared` is the Compose Multiplatform app layer for Android, Windows, and Linux:
Compose UI, feature ViewModels, and `expect`/`actual` platform bridges. Native
transfer work goes through UniFFI `VnidropCore` (see `crates/vnidrop`). Apple
platforms use the native SwiftUI app under `apple/`.
---
## VniDrop KMP UI skill (required for UI work)
For screens, components, theme, navigation, resources, ViewModel↔UI wiring,
lists, animation, accessibility:
1. Load [`.codex/skills/compose-skill/SKILL.md`](../.codex/skills/compose-skill/SKILL.md).
2. Follow its VniDrop-specific workflow and defaults.
3. Open **at most one** file under `.codex/skills/compose-skill/references/` when
the skill links to it for the current task.
4. Do **not** invent a parallel Compose style guide.
### Project policy
| Topic | Do this |
|-------|---------|
| Architecture | Keep **MVVM-style** ViewModels: immutable `*State`, `StateFlow`, **named methods**. Do not force MVI `onEvent` sealed hierarchies unless asked. |
| Structure | Feature packages under `com.vnidrop.app.feature.*`; thin route/wiring + screen/composables. |
| Theme | Only `LocalVniDropColors` / `VniDropThemeTokens` (`ui/theme/VniDropTheme.kt`). Brand primary light ≈ `#A855F7` (HSL 271, 91%, 65%). |
| Strings | CMP composeResources / `Res.string.*` — not Android `R` in `commonMain`. `values*/strings.xml` are **generated** from `localization/strings.json` (source of truth) via the loc CLI — add/edit keys there, never in the XML. |
| DI | Follow existing `AppGraph` construction; no unprompted Hilt/Koin migration. |
| Platform | `androidMain` / `jvmMain` for pickers, SAF, NFC/QR, and desktop integration. |
| Dependencies | Before adding Jetpack/AndroidX to `commonMain`, verify multiplatform artifacts for all targets. |
The skill is repository-specific. Do not substitute a generic Compose/MVI style guide.
Platform-native presentation is more important than maximizing shared UI code.
Use Material icons and conventions on Android, Fluent on Windows, and the existing
Lucide/desktop conventions on Linux. Repeated platform presentation code is
acceptable when sharing would make a platform feel foreign; domain behavior and
state machines must remain shared.
---
## Commands
From repo root:
```bash
make check-shared
```
Optional:
```bash
make test-android-host
make run-desktop
make check-android
```
CI `:shared:jvmTest` runs on **Linux**. Gobley host cargo follows the current
host and architecture so local desktop builds embed their matching Rust library.
When Kotlin changes touch UniFFI-generated APIs, rebuild/test with a full
`jvmTest` so Gobley/native pieces stay aligned.
---
## Layout
```
src/
commonMain/kotlin/com/vnidrop/app/
core/ # models, CoreGateway, FilePicker interfaces
feature/send|receive|approvals|settings|app/
ui/ # theme, components, navigation, feedback, state helpers
commonMain/composeResources/
androidMain|jvmMain/
commonTest|jvmTest|...
```
### Platform file bridging (must preserve)
- **Android share:** open content URIs as FDs; expand **folder trees** to per-file
documents with relative `displayName` paths before calling Rust
(`PickedShareSourceAdapter.android.kt` / `expandShareDirectory`). Transfer creation
uses the focused `PickedShareSourceAdapter`; `FileSystemService` owns receive storage.
- **Android receive:** MediaStore Downloads sink and/or SAF tree write sink.
- **Apple:** lives outside this module under `apple/`; do not add Apple platform
behavior back to KMP.
- **Desktop:** filesystem paths; directories may be marked `isDirectory` for Rust walk.
Never pass a directory as a single Android FD into `SourceKind.FILE_DESCRIPTOR`.
---
## Code style (Kotlin)
- Match existing feature style (imports, naming, state updates via `update { }`).
- Composables render state and invoke callbacks; no business rules in `@Composable`
bodies (network, share creation, ticket parse — ViewModel/core).
- Prefer stable list keys from domain IDs.
- Comments only for non-obvious platform or concurrency reasons.
- Do not hard-code brand colors; use theme tokens.
---
## Testing
- Logic: `src/commonTest/` (e.g. ViewModel fakes, `AppUiModels` progress helpers).
- Compose/JVM: `src/jvmTest/`.
- Add/adjust tests when changing state machines, progress aggregation, or
share/receive eligibility.
- Prefer fakes in `commonTest` support over real UniFFI in pure unit tests.
```bash
make test-shared
```
---
## Anti-patterns
- Streaming transfer bytes through Kotlin as the main design
- Directory FDs on Android
- New DI framework “because best practice”
- Loading every compose-skill reference file for a small UI tweak
- Android `R.string` in `commonMain`