aboutsummaryrefslogtreecommitdiff
path: root/src/app
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-06 23:06:00 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-06 23:06:00 +0300
commit56edcbcba83b8ff7f6f69ee4231f163e97df3346 (patch)
treee6eb1c31fd8f9a317fe1d6d49b019037f14b4746 /src/app
parentefceb30f0b447196615e3b2821d12ef82adcb3e2 (diff)
feat: expose pane scroll state
Diffstat (limited to 'src/app')
-rw-r--r--src/app/api/panes.rs42
-rw-r--r--src/app/creation.rs10
2 files changed, 52 insertions, 0 deletions
diff --git a/src/app/api/panes.rs b/src/app/api/panes.rs
index c4e6321d..60b2e93b 100644
--- a/src/app/api/panes.rs
+++ b/src/app/api/panes.rs
@@ -1877,6 +1877,22 @@ mod tests {
(app, public_pane_id, rx)
}
+ fn app_with_scrollback_runtime() -> (App, String, PaneId) {
+ let (mut app, public_pane_id) = app_with_test_workspace();
+ let pane_id = app.state.workspaces[0].tabs[0].root_pane;
+ let lines = (0..20)
+ .map(|line| format!("line {line:02}\n"))
+ .collect::<String>();
+ let runtime = crate::terminal::TerminalRuntime::test_with_scrollback_bytes(
+ 20,
+ 5,
+ 1000,
+ lines.as_bytes(),
+ );
+ app.state.insert_test_runtime(pane_id, runtime);
+ (app, public_pane_id, pane_id)
+ }
+
fn metadata_params(pane_id: String) -> PaneReportMetadataParams {
PaneReportMetadataParams {
pane_id,
@@ -1929,6 +1945,32 @@ mod tests {
}
#[tokio::test]
+ async fn api_pane_get_exposes_scroll_metrics() {
+ let (mut app, public_pane_id, pane_id) = app_with_scrollback_runtime();
+ let runtime = app
+ .state
+ .runtime_for_pane_in_workspace(&app.terminal_runtimes, 0, pane_id)
+ .expect("runtime");
+ runtime.scroll_up(3);
+
+ let response = app.handle_pane_get(
+ "req".into(),
+ PaneTarget {
+ pane_id: public_pane_id,
+ },
+ );
+
+ let success: SuccessResponse = serde_json::from_str(&response).unwrap();
+ let ResponseResult::PaneInfo { pane } = success.result else {
+ panic!("expected pane info response");
+ };
+ let scroll = pane.scroll.expect("scroll metrics");
+ assert_eq!(scroll.offset_from_bottom, 3);
+ assert!(scroll.max_offset_from_bottom >= scroll.offset_from_bottom);
+ assert_eq!(scroll.viewport_rows, 5);
+ }
+
+ #[tokio::test]
async fn api_pane_send_keys_preserves_legacy_control_c_aliases() {
let (mut app, pane_id, mut rx) = app_with_send_key_runtime(3);
diff --git a/src/app/creation.rs b/src/app/creation.rs
index bfc4cd03..13fc0a8e 100644
--- a/src/app/creation.rs
+++ b/src/app/creation.rs
@@ -375,6 +375,15 @@ impl App {
let pane = ws.pane_state(pane_id)?;
let terminal = self.state.terminals.get(&pane.attached_terminal_id)?;
let tab_idx = ws.find_tab_index_for_pane(pane_id)?;
+ let scroll = self
+ .state
+ .runtime_for_pane_in_workspace(&self.terminal_runtimes, ws_idx, pane_id)
+ .and_then(|runtime| runtime.scroll_metrics())
+ .map(|metrics| crate::api::schema::PaneScrollInfo {
+ offset_from_bottom: metrics.offset_from_bottom as u64,
+ max_offset_from_bottom: metrics.max_offset_from_bottom as u64,
+ viewport_rows: metrics.viewport_rows as u64,
+ });
let focused = self.state.active == Some(ws_idx)
&& ws.active_tab == tab_idx
&& ws
@@ -401,6 +410,7 @@ impl App {
custom_status: presentation.custom_status,
state_labels: presentation.state_labels,
agent_session: terminal_agent_session_info(terminal),
+ scroll,
revision: terminal.revision,
})
}