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

4.8 KiB

AGENTS.md — shared/ (Compose Multiplatform + KMP)

Nearest guide when editing under shared/. Root 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.
  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:

make check-shared

Optional:

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.
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