Skip to content

Web UI: pull down on the header bar to refresh on mobile#289

Merged
edwin-zvs merged 4 commits into
mainfrom
webui-pull-to-refresh
May 31, 2026
Merged

Web UI: pull down on the header bar to refresh on mobile#289
edwin-zvs merged 4 commits into
mainfrom
webui-pull-to-refresh

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

Problem

The app shell is locked to html, body { overflow: hidden; overscroll-behavior: none }, so the browser's native swipe-down-to-refresh never engages. On a narrow phone viewport (e.g. iOS / Android in portrait) the URL-bar reload button is also hidden, leaving no obvious way to refresh the page.

Hijacking page-wide swipes isn't an option — it would fight the session-view scroll-up gesture, which is the primary thing you'd want a downward swipe to do inside the chat / terminal pane.

Fix

Scope the gesture to just <header>:

  • A vertical drag that starts on the header bar grows a sticky banner from the top of the viewport ("↓ pull down to refresh" → "↑ release to refresh" past the threshold → "↻ refreshing…" while reloading).
  • Releasing past 70 px of pull reloads via location.reload(); releasing earlier snaps the banner back.
  • The handler waits for 8 px of downward movement before engaging, so taps on the hamburger toggle, session title, and connection indicator still fire normally.
  • touch-action: pan-y on <header> keeps the browser dispatching touchmove events; preventDefault is only called once tracking starts.
  • Touches that start outside the header (chat pane, session list, terminal, widgets) keep their existing behavior — scroll-up still scrolls.

All in crates/daemon/assets/index.html: a .pull-indicator CSS overlay and an initPullToRefresh() function called from boot.

Test

cargo build -p agentd   # clean

UI gesture itself isn't covered by automated tests (no headless mobile-touch harness in this repo). Manual verification on a narrow viewport:

  1. Long-press-drag down from the header bar → banner expands, label flips at ~70 px.
  2. Release past threshold → "↻ refreshing…" → page reloads.
  3. Release under threshold → banner snaps back, no reload.
  4. Tap hamburger / title → list toggles / no spurious refresh.
  5. Drag down from inside the session view → scrolls history (unchanged).

🤖 Generated with Claude Code

edwin-zvs added 4 commits May 30, 2026 17:38
The app shell is locked to `html, body { overflow: hidden }` so the
browser's native swipe-down-to-refresh never engages. On a narrow phone
viewport the URL-bar reload button is also hidden, which leaves no
obvious way to refresh the page — and hijacking page-wide swipes would
fight the session-view scroll.

Scope the gesture to the `<header>` only: a vertical drag that starts
on the title bar grows a sticky banner from the top of the viewport;
releasing past a 70 px threshold reloads. Anything starting outside the
header (chat scroll, session list, terminal) keeps its existing
behavior.

The handler differentiates from a tap by waiting until the drag has
moved more than 8 px down before it engages, so hamburger / title /
connection-indicator taps still fire normally. `touch-action: pan-y`
on the header lets the browser dispatch touchmove cleanly, and we
`preventDefault` only once tracking starts.
`location.reload()` is the wrong UX for a pull-to-refresh gesture on
mobile: it nukes the xterm scrollback, transcript scroll position,
widgets, and any in-flight PTY input — and flashes the page white for
half a second on a slow phone.

Replace it with `softRefresh()`: close the active WebSocket and let the
existing close handler -> scheduleReconnect -> connect path re-run end
to end. That re-subscribes globally, re-fetches the session list, and
hydrates the active session via the reconnect path
(`refreshCurrentSessionAfterReconnect`), which already preserves the
xterm buffer for terminal sessions. The reconnect-delay backoff is
short-circuited to 50 ms so the gesture feels instant.
You asked for the gesture to do a real refresh. The intervening
soft-refresh detour was based on misreading 'can you hot reload' as a
request to change the gesture behavior; you actually meant 'use
agentd's webui_hot_reload tool to iterate'. Drop the helper function
(its name was also clashing with the project's existing 'hot reload'
terminology for the dev asset poller) and inline the original
location.reload() back at the call site.
@edwin-zvs
edwin-zvs merged commit 925ddff into main May 31, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the webui-pull-to-refresh branch May 31, 2026 00:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant