feat(chat): /resume and /history open the session history tab (issue #864) (#876)

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-08-10 21:43:33 +03:00
1 parent f931565c0d
commit b05cffa9c4
6 files changed
+99 -1

No files matched your search

@@ -74,6 +74,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.HistoryScreen
import com.m57.hermescontrol.NavigationController
import com.m57.hermescontrol.R
import com.m57.hermescontrol.data.model.Attachment
@@ -193,6 +194,15 @@ fun ChatScreen(
}
}
// /resume · /history (issue #864): open the session history tab so the
// user picks a past session — client-side, no gateway round-trip.
LaunchedEffect(state.openHistoryRequested) {
if (state.openHistoryRequested) {
NavigationController.navigateTo(HistoryScreen)
viewModel.consumeOpenHistoryRequest()
}
}
// Arm + trigger older-message paging when the user scrolls near the top.
// Before prepending, capture the anchor (first visible message id + offset)
// so the same content stays under the reader's eye after the insert
@@ -279,6 +279,9 @@ data class ChatUiState(
// #865). Empty until the local store loads; commands without recorded
// usage keep their catalog order.
val slashUsageCounts: Map<String, Int> = emptyMap(),
// Transient nav request from /resume and /history (issue #864): the
// screen consumes it by navigating to the history tab, then clears it.
val openHistoryRequested: Boolean = false,
// In-session model picker (issue #589) — surfaced when the user types /model
// (or taps the top-bar model chip). Mirror of the global model screen's
// picker, but the selection hot-swaps the CURRENT session via the slash path.
@@ -1435,6 +1438,13 @@ class ChatViewModel(
openUpdateConfirm()
}
is SlashResult.OpenHistory -> {
// Client-side: open the session history tab so the user can
// pick a past session to resume (issue #864) — no gateway
// round-trip (the backend slash worker can't answer /resume).
_uiState.update { it.copy(openHistoryRequested = true) }
}
is SlashResult.RpcDispatch -> {
dispatchViaRpc(command)
}
@@ -3231,6 +3241,11 @@ class ChatViewModel(
_uiState.update { it.copy(backgroundCompleteMessage = null) }
}
/** Consume the /resume · /history navigation request (issue #864). */
fun consumeOpenHistoryRequest() {
_uiState.update { it.copy(openHistoryRequested = false) }
}
// ── Approval flow ───────────────────────────────────────────────────
private fun handleApprovalRequest(event: WsEvent.ApprovalRequest) {
@@ -31,6 +31,7 @@ class SlashCommandDispatcher {
"/fork", "/branch" -> SlashResult.SessionBranch
"/model" -> SlashResult.ModelSwitch
"/update" -> SlashResult.Update
"/resume", "/history" -> SlashResult.OpenHistory
else -> SlashResult.RpcDispatch
}
}
@@ -71,4 +72,12 @@ sealed class SlashResult {
* after 45s (issue #862).
*/
data object Update : SlashResult()
/**
* Open the session history tab client-side (issue #864). /resume and
* /history are CLI-flavored interactive commands the mobile gateway path
* can't answer usefully — same class of problem as /update (#862). The
* user picks a past session from history and resumes it from there.
*/
data object OpenHistory : SlashResult()
}