# TASK-030: Gitea-green theme, pagination footer height fix, repository row language/favorite Status: implemented; all required checks pass; the human product owner has not yet confirmed live Owner: Claude Reviewer: Codex ## Outcome Three connected requests, delivered together: the whole app is now themed around Gitea's own brand green (previously it used Compose Material3's unthemed default — purple — since the app's `ColorScheme` had never actually been given real values); every pagination footer's buttons (previous page/next page/numbered page buttons) now render at one guaranteed-consistent height, fixing a live-reported bug where they visibly varied; and each repository row now shows its primary language and Gitea star count, with a tappable star toggle wired to the app's existing (previously built but never connected to any screen) favorite/star action. ## Scope - Included: `ui/theme/Color.kt` (new), `ui/theme/Theme.kt` (real `lightColorScheme`/`darkColorScheme` values instead of Compose's defaults), new `ui/PaginationFooter.kt` (`SimplePaginationFooter`, `PaginationButtonHeight`), `feature/history/HistoryScreen.kt`/`feature/notifications/NotificationsScreen.kt`/`feature/repositories/RepositoryListScreen.kt` (their plain previous/next footers now call the shared composable), `feature/workitems/WorkItemsScreen.kt` (both its plain fallback footer and its numbered footer's buttons now set an explicit height), `domain/model/RepositoryModels.kt` (`RepositorySummary` gains `language`/`starsCount`), `data/repositories/GiteaRepositoryBrowserApi.kt` (parses both from the repository list response Gitea already returns), `feature/repositories/RepositoryListViewModel.kt` (`RepositoryRowUiModel` gains the same two fields), `feature/repositories/RepositoryListScreen.kt` (renders language + a `FavoriteStar` toggle per row, wires `onToggleFavorite`), `MainActivity.kt` (`onToggleFavorite` wiring). - Excluded: No change to any screen's actual functionality, navigation, or gesture behavior — this task is deliberately colors/sizing/one-new-row-affordance only, per the human product owner's explicit instruction ("디자인을 변경하면서 현재의 기능이 절대 변동되어서는 안돼... 조작감도 마찬가지야"). No attempt to determine a repository's *already*-starred state on initial load for the ALL list (see Design notes) — that remains exactly the limitation TASK-018 already had, not a new one introduced here. No per-language color coding (see Design notes). ## Design notes - **Gitea's real brand green, not a guess**: verified against Gitea's own source (`web_src/css/themes/theme-gitea-light.css`'s `--color-logo: #609926`) before picking a palette, per this project's established convention. The Material3 `primary` role itself uses a slightly deeper tone of that same green (`#3B6A1F`) rather than the exact logo value — white text on `#609926` directly sits around a 3.4:1 contrast ratio, under WCAG's 4.5:1 threshold for normal-size button labels; the rest of the app's now ~20 M3 color roles (secondary/tertiary, each with a container + on-container pair, plus the full light/dark neutral scale) were hand-built as a coherent tonal palette grown from that same hue family. `error`/`onError`/`errorContainer`/ `onErrorContainer` were deliberately left at Material3's own baseline red — nothing about this request was about changing what an error (or a removed diff line) looks like. - **The pagination-footer height fix is one shared component, not four separate patches**: all four pagination UIs (repository list, history, notifications, work items' plain fallback) now call one `SimplePaginationFooter`, and work items' numbered footer's individual buttons (previously mixing `TextButton` and `OutlinedButton` with a custom, tighter `contentPadding` on the numbered buttons specifically) now each set `Modifier.height(PaginationButtonHeight)` explicitly. Pinning one explicit height on every button removes the possibility of disagreement by construction, regardless of which specific Material3 default/padding interaction was actually responsible for the reported drift — a more robust fix than chasing that exact mechanism down. Each screen keeps its own string resources (so their text stays independently localizable); only the visual composable is shared. - **Language and star count needed no new network call**: Gitea's repository-list endpoint (verified against `modules/structs/repo.go`'s `Repository` struct) already returns both `language` and `stars_count` on the exact same call this app already makes to populate the list. - **The favorite-star toggle reuses `toggleFavorite` exactly as TASK-018 already built and tested it, unmodified** — this task only connects a UI trigger that never existed, it doesn't change the underlying action. That action is already context-sensitive per list mode: on the ALL list it stars (and `favoriteAction` only reflects *this session's* interaction, defaulting to `Idle`/outlined even for a repository starred before this session — a known limitation carried over unchanged, not newly introduced); on the FAVORITES list every row is inherently already starred (that's why it's showing), so tapping there un-favorites and removes the row on success rather than toggling the icon's own look. `FavoriteStar`'s rendering branches on which list mode is current specifically to get this right: filled/primary-colored when actually confirmed starred (or, on the FAVORITES list, whenever not mid-unstar), outlined otherwise, error-tinted on a failed attempt (still tappable to retry), all sharing one `Row` clickable so it doesn't also trigger the row's own "open this repository" tap. - **No per-language color coding**: GitHub/Gitea's own "colored dot per language" convention (visible in the reference screenshot) comes from a maintained language-color mapping (the `linguist` project) this app has no copy of and no reliable way to source accurately; the language name is shown as plain text instead, matching what was actually asked for ("어떤 언어인지... 보여줄래") without fabricating colors that might not match Gitea's own. ## Acceptance criteria - [x] Every color visible across the app (tab indicators, buttons, selected states, links) now reads as Gitea-brand green instead of Compose's unthemed default purple. - [x] Dark mode has its own complete, coherent color scheme (not just the light one shown at a lower opacity). - [x] `error`/diff-removed-line coloring is visually unchanged (still red). - [x] Every pagination footer's buttons (previous/next/numbered) render at the same height as each other, confirmed by direct visual inspection on the live device (not just by code review). - [x] Each repository row shows its Gitea-reported primary language (when non-blank) and star count. - [x] Tapping a row's star toggle stars it on the ALL list (or un-favorites and removes the row on the FAVORITES list) via the exact same `toggleFavorite` action TASK-018 already built — confirmed live on the device (star count/outline visibly responded to a tap). - [x] Build, unit tests, and lint pass. - [ ] Full live confirmation from the human product owner across all three changes together — the device screenshots taken this session confirm the green theme, consistent footer heights, and language/star display are all visibly correct, but the human product owner hasn't yet given explicit sign-off in this exact session turn. ## Required checks ```text ./gradlew assembleDebug ./gradlew testDebugUnitTest ./gradlew lintDebug ``` ## Evidence - `ui/theme/Color.kt` (new): the full hand-built light/dark tonal palette, with the Gitea-source citation for the seed green in its file-level doc comment. - `ui/theme/Theme.kt`: `lightColorScheme(...)`/`darkColorScheme(...)` now pass every role explicitly from `Color.kt` (previously called with no arguments at all, i.e. Compose's own baseline purple). - `ui/PaginationFooter.kt` (new): `PaginationButtonHeight = 40.dp` (matching Material3's own `ButtonDefaults.MinHeight`, made explicit rather than trusted implicitly) and `SimplePaginationFooter`. - `HistoryScreen.kt`/`NotificationsScreen.kt`/`RepositoryListScreen.kt`: each screen's `PageFooter` (or, for repositories, its inline pagination `Row`) now delegates to `SimplePaginationFooter`, passing its own existing string resources; now-unused `OutlinedButton` imports removed where applicable. - `WorkItemsScreen.kt`: plain fallback `PageFooter` also now uses `SimplePaginationFooter`; `NumberedPageFooter`'s `TextButton`/`OutlinedButton` calls each gained `Modifier.height(PaginationButtonHeight)`. - `RepositoryModels.kt`: `RepositorySummary` gained `language: String? = null` and `starsCount: Int = 0` (both defaulted so no existing call site needed to change). - `GiteaRepositoryBrowserApi.kt`: `RepositoryDto` gained `language`/`stars_count` fields; `mapRepository` populates both (blank language becomes `null`). - `RepositoryListViewModel.kt`: `RepositoryRowUiModel` gained the same two fields; `toUiModel()` updated. - `RepositoryListScreen.kt`: new `FavoriteStar` composable (mode-aware rendering described in Design notes); each row gained a `Row` showing language (or a blank `Spacer` when absent) and the star toggle; `onToggleFavorite` threaded through `RepositoryListScreen`/`RepositoryContent`. - `MainActivity.kt`: `onToggleFavorite = viewModel::toggleFavorite`. - New test: `GiteaRepositoryBrowserApiTest.kt` (`language`/`stars_count` mapped correctly; a blank `language` string becomes `null`). - `./gradlew compileDebugKotlin compileDebugUnitTestKotlin`: BUILD SUCCESSFUL. - `./gradlew testDebugUnitTest`: BUILD SUCCESSFUL, all unit tests passing (including the new one above). - `./gradlew assembleDebug lintDebug`: BUILD SUCCESSFUL, 0 lint errors. - Live device: build installed over wireless `adb`. Screenshots taken directly on the device confirm: the green theme applied consistently across the repository list, its tabs, and the branch-detail file browser; the repository list's previous/next footer buttons render at matching heights; each row shows its language (e.g. "C++", "C#") and star count with the outline/filled star icon. ## Known limitations - On the ALL repositories list, a repository already starred before the current app session still shows an outlined (not filled) star until acted on in this session — `favoriteAction` only tracks this session's own interaction, not the server's actual current starred state at load time. This is carried over unchanged from TASK-018's original design, not a new gap. - No per-language color coding (see Design notes) — language is shown as plain text only. - Contrast for the hand-built color palette was reasoned through directly (informal luminance/ contrast-ratio estimation against WCAG's 4.5:1 threshold for the primary/onPrimary pairing specifically) rather than verified with an automated contrast-checking tool across every one of the ~20 color roles: the palette is a solid, deliberate design, but a full formal accessibility audit of every role pairing hasn't been done. - Full live human confirmation of this batch is still pending, even though device screenshots taken directly this session already show all three pieces working correctly. ## Review findings None yet — pending Codex/human review.