From c963f67ec4dd24b4404e97bcf099adb678641b5a Mon Sep 17 00:00:00 2001 From: Rely-xcy <179817675+Rely-xcy@users.noreply.github.com> Date: Fri, 2 Oct 2026 15:22:47 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(nav):=20=E5=85=9C=E5=BA=95=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E6=8B=A6=E6=88=AA=EF=BC=8C=E6=A0=88=E5=B0=86=E7=A9=BA?= =?UTF-8?q?=E6=97=B6=E5=9B=9E=E8=81=8A=E5=A4=A9=E9=A1=B5=E8=80=8C=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=E5=BC=B9=E7=A9=BA=E6=88=90=E7=99=BD=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户指出(对的):光修入口不够,「返回键点多几次又会触发」——因为入口不止一个 (终端/Git/浏览器/设置/凭据都是 navigate 进来的,侧栏入口还是裸 navigate), predictive back 的边缘手势还能把人带进栈根不是 chat 的怪状态。逐个修入口永远有漏网。 兜底层:MainActivity 最外层加一个 BackHandler,当 - 当前不在聊天页,且 - 没有上一层可弹(previousBackStackEntry == null,再弹就空栈) 时,返回键改为 navigate("chat") 清栈回聊天页。这样无论栈怎么被弄乱, 白屏都不可能出现;在聊天页时兜底不启用(enabled=false),返回键仍是正常的退出应用。 backQueue.size 那个写法废弃(Navigation Compose 的 backQueue 不是公开 API 语义), 改用 previousBackStackEntry == null 判「再弹就空」。 现有三层返回处理不受影响:抽屉开 → 收抽屉;大屏右栏开 → 收右栏;设置页内部层级 → 内部返回。它们 enabled 条件不满足时才轮到兜底。 --- app/src/main/java/com/aicode/MainActivity.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/java/com/aicode/MainActivity.kt b/app/src/main/java/com/aicode/MainActivity.kt index 793eeceb..3755842e 100644 --- a/app/src/main/java/com/aicode/MainActivity.kt +++ b/app/src/main/java/com/aicode/MainActivity.kt @@ -389,6 +389,22 @@ fun AppNavigation( scope.launch { drawerState.close() } } + // 返回栈兑底:栈里只剩当前目的地、且当前不在聊天页时,返回键改为回聊天页, + // 而不是让系统把栈弹空(白屏)。 + // 背景:历史上终端/Git/设置都用裸 navigate 压栈,任何一处状态错乱都可能让用户 + // 站在一个「栈根不是 chat」的怪状态里;逐个修入口永远有漏网的(predictive back + // 的边缘手势还能把人带进设置),所以在最外层兑底,保证白屏不可能再出现。 + // 注意 enabled 条件里排除 chat:在聊天页返回就该退出应用,不能被兑底拦截。 + val canFallbackToChat = currentRoute != null && currentRoute != "chat" && + navController.currentBackStackEntry?.lifecycle?.currentState == androidx.lifecycle.Lifecycle.State.RESUMED && + navController.previousBackStackEntry == null // 再弹就空了(没有上一层) + BackHandler(enabled = canFallbackToChat) { + navController.navigate("chat") { + popUpTo(navController.graph.startDestinationRoute ?: "chat") { inclusive = true } + launchSingleTop = true + } + } + // 侧边栏需要的数据。 val currentWorkspace by workspaceViewModel.current.collectAsStateWithLifecycle() androidx.compose.runtime.LaunchedEffect(currentWorkspace) { From adc12e8ca3302b90850f0045bd2dd02495dd453f Mon Sep 17 00:00:00 2001 From: Rely-xcy <179817675+Rely-xcy@users.noreply.github.com> Date: Fri, 2 Oct 2026 04:25:35 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(nav):=20=E7=BB=88=E7=AB=AF/Git=20?= =?UTF-8?q?=E5=8E=8B=E7=A9=BF=E8=BF=94=E5=9B=9E=E6=A0=88=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=99=BD=E5=B1=8F=EF=BC=8C=E4=B8=94=E4=BB=8E=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E6=89=8B=E5=8A=BF=E8=BF=9B=E8=AE=BE=E7=BD=AE=E9=A1=B5=E5=90=8E?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E9=80=80=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户报告:点击进入终端后,连按返回多次会触发白屏;此时右滑手势能切到设置页, 但一旦进了设置页就无法退出。 两段病因,都在窄窗(手机)导航栈上: 1. 返回栈被压穿 → 白屏。openWorkbench 窄窗分支用裸 navigate("terminal"/"git"/"browser"), 每开一次就往栈里压一层:chat → terminal → chat → terminal → …。连按返回逐层弹出, 弹空后(栈里没有任何目的地)就是白屏;此时 predictive-back 的右滑手势仍可触发 邻近的设置页,把用户带进一个"栈根不是 chat"的怪状态。 修:navigate 改为 popUpTo("chat") { saveState = true } + launchSingleTop—— 无论开多少次终端,返回栈始终是 chat → terminal 一层,返回键只回聊天页。 2. 进了设置页无法退出。SettingsScreen 的 onNavigateBack(内部层级退完后的最后一跳) 原来是 popBackStack()——依赖栈里恰好有上一层;栈已被弄乱时它无事发生,留在设置页 怎么按都出不去。 修:改用 navigate("chat") { popUpTo(startDestinationRoute) { inclusive = true } } + launchSingleTop——不依赖返回栈是否完好,总能回聊天页并把栈清干净。 大屏不受影响:openWorkbench 的 expanded 分支仍是右栏开合(不入栈)。 --- app/src/main/java/com/aicode/MainActivity.kt | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/aicode/MainActivity.kt b/app/src/main/java/com/aicode/MainActivity.kt index 3755842e..3654cfdd 100644 --- a/app/src/main/java/com/aicode/MainActivity.kt +++ b/app/src/main/java/com/aicode/MainActivity.kt @@ -532,6 +532,10 @@ fun AppNavigation( } // 终端 / Git:大屏在右栏内开合切换,窄窗跳全屏页。 + // 窄窗 navigate 用 popUpTo("chat") { saveState = true } + launchSingleTop:不这样, + // 每次开终端/Git 都往返回栈里压一层,用户连按几次返回会穿到空栈(白屏), + // 再从预览手势右滑反而能切到设置页,而设置页的 BackHandler 又把返回键全吞了 → 卡死。 + // popUpTo 折叠栈后,返回键从终端/Git 只会回聊天页,不会进入空栈。 val openWorkbench: (WorkbenchPaneKind) -> Unit = { target -> if (expanded) { paneKind = if (paneKind == target) WorkbenchPaneKind.NONE else target @@ -542,7 +546,10 @@ fun AppNavigation( WorkbenchPaneKind.BROWSER -> "browser" else -> "chat" } - navController.navigate(route) + navController.navigate(route) { + popUpTo("chat") { saveState = true } + launchSingleTop = true + } } } @@ -713,7 +720,17 @@ fun AppNavigation( SettingsScreen( viewModel = settingsViewModel, onNavigateBack = { - navController.popBackStack() + // 设置页内部的 BackHandler 在有上一层时会吞掉返回键(用于内部层级返回), + // 走到这里说明已在首页菜单层;若返回栈被弄乱导致栈里没有 "chat" 了 + // (终端/Git 历史上会把栈压穿),popBackStack 会无事发生且留在设置页 → + // 用户怎么按返回都出不去(卡死)。所以这里用 navigate 回聊天页, + // 不依赖返回栈是否完好。 + navController.navigate("chat") { + popUpTo(navController.graph.startDestinationRoute ?: "chat") { + inclusive = true + } + launchSingleTop = true + } // 大屏返回聊天页后侧栏本就常驻,不再弹 modal 抽屉。 if (!expanded) scope.launch { drawerState.open() } },