terminal: Prevent scrollbar arithmetic underflow panic (#45282)

rabsef-bicrym created

## Summary

Fixes arithmetic underflow panics in `terminal_scrollbar.rs` by
converting unsafe subtractions to `saturating_sub`.

Closes #45281

## Problem

Two locations perform raw subtraction on `usize` values that panic when
underflow occurs:

- `offset()`: `state.total_lines - state.viewport_lines -
state.display_offset`
- `set_offset()`: `state.total_lines - state.viewport_lines`

This happens when `total_lines < viewport_lines + display_offset`, which
can occur during terminal creation, with small window sizes, or when
display state becomes stale.

## Solution

Replace the two unsafe subtractions with `saturating_sub`, which returns
0 on underflow instead of panicking.

Also standardizes the existing `checked_sub().unwrap_or(0)` in
`max_offset()` to `saturating_sub` for consistency across the file.

## Changes

- N/A

Change summary

crates/terminal_view/src/terminal_scrollbar.rs | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)

Detailed changes

crates/terminal_view/src/terminal_scrollbar.rs 🔗

@@ -50,28 +50,24 @@ impl ScrollableHandle for TerminalScrollHandle {
         let state = self.state.borrow();
         size(
             Pixels::ZERO,
-            state
-                .total_lines
-                .checked_sub(state.viewport_lines)
-                .unwrap_or(0) as f32
-                * state.line_height,
+            state.total_lines.saturating_sub(state.viewport_lines) as f32 * state.line_height,
         )
     }
 
     fn offset(&self) -> Point<Pixels> {
         let state = self.state.borrow();
-        let scroll_offset = state.total_lines - state.viewport_lines - state.display_offset;
-        Point::new(
-            Pixels::ZERO,
-            -(scroll_offset as f32 * self.state.borrow().line_height),
-        )
+        let scroll_offset = state
+            .total_lines
+            .saturating_sub(state.viewport_lines)
+            .saturating_sub(state.display_offset);
+        Point::new(Pixels::ZERO, -(scroll_offset as f32 * state.line_height))
     }
 
     fn set_offset(&self, point: Point<Pixels>) {
         let state = self.state.borrow();
         let offset_delta = (point.y / state.line_height).round() as i32;
 
-        let max_offset = state.total_lines - state.viewport_lines;
+        let max_offset = state.total_lines.saturating_sub(state.viewport_lines);
         let display_offset = (max_offset as i32 + offset_delta).clamp(0, max_offset as i32);
 
         self.future_display_offset