diff --git a/app/src/main/java/com/ubisam/ubgitea/feature/workitems/WorkItemsScreen.kt b/app/src/main/java/com/ubisam/ubgitea/feature/workitems/WorkItemsScreen.kt
index 3a8f0d5..4aeceae 100644
--- a/app/src/main/java/com/ubisam/ubgitea/feature/workitems/WorkItemsScreen.kt
+++ b/app/src/main/java/com/ubisam/ubgitea/feature/workitems/WorkItemsScreen.kt
@@ -349,12 +349,24 @@ private fun NumberedPageFooter(page: Int, totalPages: Int, onPrevious: () -> Uni
fun submit() {
input.toIntOrNull()?.let { onGoToPage(it.coerceIn(1, totalPages)) }
}
+ // Submitting still clamps regardless (coerceIn above), so an out-of-range value never
+ // requests a page that doesn't exist -- this only makes that clamping visible before
+ // the tap, instead of the field silently accepting e.g. "999" and quietly landing on
+ // the last page with no explanation.
+ val typedPage = input.toIntOrNull()
+ val isOutOfRange = typedPage != null && typedPage !in 1..totalPages
Text("[", style = MaterialTheme.typography.bodyLarge)
OutlinedTextField(
value = input,
onValueChange = { input = it.filter(Char::isDigit).take(6) },
modifier = Modifier.width(72.dp),
singleLine = true,
+ isError = isOutOfRange,
+ supportingText = if (isOutOfRange) {
+ { Text(stringResource(R.string.workitems_page_jump_out_of_range, totalPages)) }
+ } else {
+ null
+ },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Go),
keyboardActions = KeyboardActions(onGo = { submit() }),
)
diff --git a/app/src/main/res/values/strings_workitems.xml b/app/src/main/res/values/strings_workitems.xml
index 95866f5..122060e 100644
--- a/app/src/main/res/values/strings_workitems.xml
+++ b/app/src/main/res/values/strings_workitems.xml
@@ -9,6 +9,7 @@
%1$d페이지
/ %1$d ]
이동
+ 1~%1$d 사이의 페이지를 입력하세요.
%1$s · 댓글 %2$d개
작성자: %1$s
(알 수 없는 사용자)
diff --git a/docs/development-log.md b/docs/development-log.md
index 3ca38e3..3c1721a 100644
--- a/docs/development-log.md
+++ b/docs/development-log.md
@@ -659,3 +659,16 @@ This is the append-only implementation and handoff record shared by Codex and Cl
- Known limitations: still session-scoped (re-fetches on a fresh session, same as every other cached list in this app); doesn't reflect a starred-set change made elsewhere (Gitea web, another device) until the next pull-to-refresh or re-login.
- Next recommended task: this closes a real, previously-documented gap rather than adding new surface area. Remaining work across this whole session is now mostly live-confirmation of already-built features (TASK-027 through TASK-036) that haven't been directly exercised by the human product owner yet.
- Next owner: Claude, continuing per the standing direction.
+
+## 2026-08-29 (continued) — TASK-028 follow-up: visible feedback for an out-of-range page-jump
+
+- Agent and role: Claude, direct continuation of the same live session — "계속 이어서 개발해줘 멈추지말고" (keep developing, don't stop). Picked up a small, contained item TASK-028 itself had documented as a known limitation.
+- Commit/diff: on `claude/task-015-notifications`; `feature/workitems/WorkItemsScreen.kt`, `strings_workitems.xml`, `tasks/028-issues-prs-numbered-pagination.md`.
+- Fix implemented: the Issues/PR numbered-pagination page-jump field previously accepted any typed number and silently clamped it to `[1, totalPages]` only on submit (typing "999" would quietly land on the last page with no explanation). It now sets `isError`/`supportingText` on the field whenever the currently-typed value falls outside that range, making the clamp visible before the tap instead of after.
+- How it was implemented: pure Compose-only change (`isOutOfRange` derived from the field's own current text, compared against `totalPages`) — submitting still clamps exactly as before, this only adds visible feedback. No test coverage added since this file's pagination UI has never had dedicated tests (matches this codebase's established pattern of not unit-testing pure Compose screens).
+- Files changed: `WorkItemsScreen.kt`, `strings_workitems.xml`, `tasks/028-issues-prs-numbered-pagination.md`.
+- Checks and results: `./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug` BUILD SUCCESSFUL, all unit tests passing, 0 lint errors. Installed on the human product owner's device over wireless `adb`.
+- Review findings and resolutions: None from a separate review pass.
+- Known limitations: same as the rest of TASK-028 — not yet seen live against a repository with enough real issues/PRs to populate the numbered UI, since no such repository was found this session.
+- Next recommended task: this was a small, contained polish item. Larger remaining candidates (e.g. a reactive 401-retry fallback at the network layer, per-repository notification settings) were considered but judged too large/risky for unrequested autonomous continuation without a dedicated task scoping pass — flagging them for the human product owner's direction rather than proceeding further unrequested into security-adjacent or larger-surface-area territory.
+- Next owner: Claude, continuing per the standing direction once the human product owner responds, or awaiting live confirmation of everything already built this session.
diff --git a/tasks/028-issues-prs-numbered-pagination.md b/tasks/028-issues-prs-numbered-pagination.md
index 27611c7..fac65fd 100644
--- a/tasks/028-issues-prs-numbered-pagination.md
+++ b/tasks/028-issues-prs-numbered-pagination.md
@@ -107,6 +107,10 @@ previous/next, since Gitea's endpoints for those don't report a total count —
requests, so the numbered-pagination UI itself was never actually populated with real data this
session (see Known limitations) — confirmed instead that the screen doesn't crash and correctly
shows the existing empty state on all four.
+- Same-session follow-up: `WorkItemsScreen.kt`'s page-jump field gained `isError`/`supportingText`
+ for an out-of-range typed value; `strings_workitems.xml` gained `workitems_page_jump_out_of_range`.
+ `./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug`
+ BUILD SUCCESSFUL; installed on-device, no crash.
## Known limitations
@@ -115,9 +119,18 @@ previous/next, since Gitea's endpoints for those don't report a total count —
requests. The header-parsing, total-page-count, and jump-to-page logic are covered by unit tests
instead, but the actual rendered page-button row and jump field haven't been seen with real
multi-page data yet.
-- The page-jump text field accepts any positive integer typed, including one exceeding
+- ~~The page-jump text field accepts any positive integer typed, including one exceeding
`totalPages` — it's clamped before being submitted (`coerceIn(1, totalPages)`), so this can't
- request an out-of-range page, but the field itself doesn't visually reject the keystroke.
+ request an out-of-range page, but the field itself doesn't visually reject the keystroke.~~
+ **Addressed in a same-session follow-up**: the field now sets `isError`/`supportingText` (a new
+ string, `workitems_page_jump_out_of_range`) whenever the currently-typed number falls outside
+ `[1, totalPages]`, so the clamp-on-submit behavior is now visible before the tap instead of a
+ silent "999 quietly becomes the last page." Submitting still clamps regardless — this only adds
+ visible feedback, it doesn't change what actually happens on submit. Like the rest of this
+ feature, this specific change hasn't been seen live against real multi-page data either (same
+ blocker as the item above), but it's a small, self-contained Compose-only change consistent with
+ this file's existing, unit-tested `coerceIn` behavior — build/lint pass, installed on-device with
+ no crash.
## Review findings