# Details Feature

Repository detail screen — owner, stats, releases with download/install, readme (with translation), per-app install/update flow. Most complex feature.

## Structure

```
feature/details/
├── domain/       # DetailsRepository, TranslationRepository; ReleaseCategory, RepoStats, SupportedLanguage, TranslationResult
├── data/         # impls + ReadmeLocalizationHelper, preprocessMarkdown
└── presentation/
    ├── DetailsViewModel.kt / State / Action / Event / Root
    ├── model/    # DownloadStage, InstallLogItem, LogResult, ShowDowngradeWarning, SupportedLanguages, TranslationState
    ├── components/
    │   ├── AppHeader, ReleaseAssetsPicker, VersionPicker, VersionTypePicker, SmartInstallButton, InspectApkButton, ApkInspectSheet, LanguagePicker, TranslationControls, StatItem
    │   └── sections/ About, Header, Logs, Owner, ReportIssue, Stats, WhatsNew, ReleaseChannel
    ├── states/ErrorState
    └── utils/    # MarkdownImageTransformer, MarkdownUtils, SystemArchitecture, LocalTopbarLiquidState, LogResultAsText
```

## Navigation

`GithubStoreGraph.DetailsScreen(repositoryId, owner, repo, isComingFromUpdate, sourceHost)`. By ID or owner+name (deep links use latter; `repositoryId == -1` falls back to owner+name lookup). `sourceHost` non-null routes all `DetailsRepository` calls through `ForgejoClientRegistry` (Codeberg / Forgejo / custom forge).

## Notes

- Readme localized via `ReadmeLocalizationHelper`; markdown via `multiplatform-markdown-renderer` + `MarkdownImageTransformer`. Translation via `TranslationRepository` + `LanguagePicker`. `TranslationProvider` enum = `GOOGLE` / `YOUDAO` / `LIBRE_TRANSLATE` / `DEEPL` / `MICROSOFT`.
- Download stages: `DownloadStage` idle→downloading→installing→done. `SmartInstallButton` adapts to install state. Downgrade warning before installing older version.
- Injects (lots): `DetailsRepository`, `TranslationRepository`, `FavouritesRepository`, `StarredRepository`, `InstalledAppsRepository`, `SeenReposRepository`, `TweaksRepository`, `TelemetryRepository`, `ExternalImportRepository`, `AuthenticationState`, `ProfileRepository`, `Downloader`, `Installer`, `PackageMonitor`, `SystemInstallSerializer`, `BrowserHelper`, `ShareManager`, `Platform`, `SyncInstalledAppsUseCase`, `InstallationManager`, `AttestationVerifier`, `DownloadOrchestrator`, `ApkInspector`, `GitHubStoreLogger`. Data layer additionally injects `ForgejoClientRegistry`.
- Android installer paths: Default / Shizuku / Dhizuku / Root. Root via raw `su` (`RootServiceManager`). Dhizuku 14+ retries without installer attribution.
- **Multi-OS picker (E15):** `ReleaseAssetsItemsPicker` toggle flips `TweaksRepository.showAllPlatforms`. ON → assets group by `assetPlatformOf` into `PlatformSectionCard`s with "Your device"/"For transfer" chips. Non-current asset → `OnDownloadForTransfer` → `BrowserHelper.openUrl`.
- **Coachmarks:** APK Inspect button pulse + ReleaseChannel chip Popup. One-shot via `TweaksRepository.get*CoachmarkShown`.
- **Self-owned ✓ badge (E20):** `AppHeader` ✓ next to owner login when `state.isCurrentUserOwner`. Reactive via `combine(profileRepo.getUser(), state.repository.owner.login)`.
- **Skip release (E542):** per-app `skippedReleaseTag` on `InstalledApp`. `SmartInstallButton` suppresses CTA; auto-clears on strictly-newer release.
- **Forgejo / Codeberg branch:** `DetailsRepositoryImpl.getRepositoryByOwnerAndName / getAllReleases / getReadme / getRepoStats / getLatestPublishedRelease` accept `sourceHost: String? = null`. Non-null → `ForgejoApiClient` direct call (no backend mediator). README from `/contents/README.md?ref={branch}` (Forgejo lacks `/readme`); license sniffed from `/contents/LICENSE` via SPDX regex; downloads aggregated by summing asset `download_count`. CRLF release bodies normalized so GFM tables render. Foreign-source `getUserProfile` skipped (GitHub-only). `AppHeader` avatar falls back to `repository.owner.avatarUrl` when profile null.
- **Multi-flavor primary picker (#638):** `pickPrimaryInstalledApp` prefers asset-filter match → `isUpdateAvailable=false` variant → `first()`. Prevents false "Update" CTA when a project ships multiple packages (e.g. generic + Play APK) and only one variant is current.
- **Content width:** `LocalContentWidth` (`COMPACT` 680dp default, `WIDE` 960dp, `EXTRA_WIDE` fills window). Set in `Tweaks → Appearance` (desktop). Outer Box's `Modifier.scrollable(state=listState, reverseDirection=true, enabled = !ANDROID)` forwards mouse-wheel events from empty side gutters to the LazyColumn — gated to non-Android to avoid double scrollable contending with LazyColumn touch.
- **Markdown perf:** Chunked progressive rendering (`splitMarkdownIntoChunks`, ~4000 chars). Pre-processing (theme-aware images + `separateAdjacentImageLinks`) on `Dispatchers.Default`. `onAction` lambda hoisted via `remember`; `MarkdownComponents` memoized; `TranslationState @Immutable`. Together kills download-progress-driven recomp storms.
- **Markdown image link awareness:** `LinkAwareMarkdownImage` walks `ASTNode.parent` for `INLINE_LINK`, makes badge images clickable to outer href. `MarkdownImageTransformer` adds browser-like User-Agent + accept header to bypass CDN hotlink protection on common badge services.
