mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-13 05:49:57 +02:00
Add the VniDrop-specific Compose architecture skill, unify invitation and targeted transfer drafts, and promote Saved devices to an adaptive first-class destination.
134 lines
4.8 KiB
Markdown
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`
|