Skip to content

fix: improve mobile touch responsiveness for session list items#1110

Closed
sheng-di wants to merge 1 commit intonesquena:masterfrom
sheng-di:fix/mobile-session-tap
Closed

fix: improve mobile touch responsiveness for session list items#1110
sheng-di wants to merge 1 commit intonesquena:masterfrom
sheng-di:fix/mobile-session-tap

Conversation

@sheng-di
Copy link
Copy Markdown
Contributor

Problem

On iPad and other touch devices, tapping a session in the left sidebar doesn't navigate. Symptoms:

  • First tap: only a style change (background highlight), no navigation
  • Second tap: sometimes works, depending on where you tap
  • Double tap: triggers rename instead of navigation

The root cause is two-fold:

1. CSS layout shift on :hover disrupts click targeting

When .session-item is hovered, padding-right changes from 8px to 40px. On iPad, touch immediately activates :hover, causing the element to reflow during the tap. The subsequent click event lands on .session-actions (the actions button that just became visible), which has e.stopPropagation(), so the navigation click never fires.

2. onclick/ondblclick are mouse-centric

The old onclick + setTimeout(220ms) + ondblclick pattern works on desktop but is unreliable on touch. iPad Safari's click-to-dblclick synthesis has timing quirks that make single-tap navigation inconsistent.

Fix

  • CSS: Remove :hover from the padding-right: 40px rule — hover only changes background and color now, no layout shift
  • CSS: Add touch-action: manipulation and -webkit-tap-highlight-color: transparent to .session-item — eliminates the 300ms tap delay and disables double-tap zoom
  • JS: Replace onclick/ondblclick with onpointerup + manual double-tap detection (350ms window). pointerup fires immediately on both mouse and touch, with no delay or ghost-click issues

Files changed

  • static/style.css — 2 lines changed
  • static/sessions.js — replaced click/dblclick with pointerup handler

iPad Safari has known issues with the click/dblclick pattern on touch:
- :hover-triggered padding-right layout shift causes the first tap click
  to target the wrong element (actions button that just appeared)
- No touch-action:manipulation means iOS still delays taps for
  double-tap zoom detection
- The old onclick+ondblclick pattern is designed for mouse, not touch

Changes:
- CSS: Remove :hover from padding-right rule to prevent layout shift
- CSS: Add touch-action:manipulation and -webkit-tap-highlight-color
  to .session-item for immediate tap response
- JS: Replace onclick/ondblclick with onpointerup + manual 350ms
  double-tap detection — works consistently on mouse and touch
@nesquena-hermes nesquena-hermes added the bug Something isn't working label Apr 26, 2026
@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Good diagnosis of a tricky touch interaction bug. The two root causes identified are accurate:

  1. CSS layout shift on :hover reflows the element during tap, displacing the click target into .session-actions which stops propagation
  2. onclick/ondblclick timing quirks on iPad Safari are well-known — the 300ms delay and double-tap detection are unreliable on touch

The fix approach is sound:

  • Removing layout-affecting CSS from :hover prevents the reflow entirely
  • touch-action: manipulation + pointer-events eliminates the tap delay without needing fastclick or similar polyfills
  • Switching to pointerup with manual double-tap detection (350ms window) gives consistent behavior across mouse and touch

Minor callout: 350ms for double-tap detection is slightly above the typical 300ms iOS double-tap threshold — worth testing on real hardware to confirm it doesn't feel sluggish for intentional double-taps (rename), and that 350ms isn't so long that misfire renames appear on slow single taps. That's a hardware UX test, not a code concern.

Overall this is a clean fix for a real friction point on iPad/touch setups.

@nesquena-hermes nesquena-hermes added the merge soon Reviewed and queued for next release batch label Apr 26, 2026
nesquena-hermes added a commit that referenced this pull request Apr 26, 2026
… x2 (#1117)

* fix(#1096): copy buttons fall back to execCommand on HTTP contexts

- Add _copyText() helper: tries navigator.clipboard first, falls back to
  document.execCommand('copy') with hidden textarea when not in secure context
- Update copyMsg() and addCopyButtons() to use helper instead of direct
  navigator.clipboard.writeText()
- Code block copy button now has .catch() handler (was silently failing)
- Error messages use t('copy_failed') for i18n instead of hardcoded string
- Add copy_failed key to all 6 locale blocks (en, ru, es, de, zh, zh-Hant)
- Add 10 regression tests

* fix(#1095): render pasted/dragged images as inline preview instead of paperclip badge

- User message attachments with image extensions now render as <img> via
  api/media endpoint, with click-to-fullscreen support
- Non-image attachments still show paperclip + filename badge
- Extracts filename from full path for display
- Add 5 regression tests

* fix: hoist _IMAGE_EXTS to module scope, add avif (absorb fix)

* fix: improve mobile touch responsiveness for session list items

iPad Safari has known issues with the click/dblclick pattern on touch:
- :hover-triggered padding-right layout shift causes the first tap click
  to target the wrong element (actions button that just appeared)
- No touch-action:manipulation means iOS still delays taps for
  double-tap zoom detection
- The old onclick+ondblclick pattern is designed for mouse, not touch

Changes:
- CSS: Remove :hover from padding-right rule to prevent layout shift
- CSS: Add touch-action:manipulation and -webkit-tap-highlight-color
  to .session-item for immediate tap response
- JS: Replace onclick/ondblclick with onpointerup + manual 350ms
  double-tap detection — works consistently on mouse and touch

* fix(#1106): iterate custom_providers[].models dict keys for dropdown population

- After reading singular 'model' field, also iterate 'models' dict keys
- Deduplicate: model field value not repeated if also in models dict
- Skip non-string keys gracefully
- Works for both named and unnamed custom_providers entries
- Add 7 regression tests

* fix(#1105): allow custom_providers hostnames through SSRF check

- Build trusted hostname set from custom_providers[].base_url in config.yaml
- These are user-explicitly configured endpoints — not SSRF risks
- Hardcoded allowlist (ollama, localhost, 127.0.0.1, lmstudio) still active
- Unknown private IPs still blocked
- Add 7 tests (5 source analysis + 2 functional with mocked socket)

* fix(tests): update hover padding assertions for #1110 touch fix (absorb)

* fix(css): restore hover padding via @media (hover:hover) for mouse devices (absorb)

* fix: filter right/middle-click from pointerup handler (absorb)

* docs: v0.50.221 release notes and version bump

---------

Co-authored-by: bergeouss <[email protected]>
Co-authored-by: nesquena-hermes <[email protected]>
Co-authored-by: sheng <[email protected]>
@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Merged in v0.50.221 via PR #1117. Thank you @sheng-di — great contribution (mobile touch responsiveness fix)! 🎉

1 similar comment
@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Merged in v0.50.221 via PR #1117. Thank you @sheng-di — great contribution (mobile touch responsiveness fix)! 🎉

JKJameson pushed a commit to JKJameson/hermes-webui that referenced this pull request Apr 29, 2026
… x2 (nesquena#1117)

* fix(nesquena#1096): copy buttons fall back to execCommand on HTTP contexts

- Add _copyText() helper: tries navigator.clipboard first, falls back to
  document.execCommand('copy') with hidden textarea when not in secure context
- Update copyMsg() and addCopyButtons() to use helper instead of direct
  navigator.clipboard.writeText()
- Code block copy button now has .catch() handler (was silently failing)
- Error messages use t('copy_failed') for i18n instead of hardcoded string
- Add copy_failed key to all 6 locale blocks (en, ru, es, de, zh, zh-Hant)
- Add 10 regression tests

* fix(nesquena#1095): render pasted/dragged images as inline preview instead of paperclip badge

- User message attachments with image extensions now render as <img> via
  api/media endpoint, with click-to-fullscreen support
- Non-image attachments still show paperclip + filename badge
- Extracts filename from full path for display
- Add 5 regression tests

* fix: hoist _IMAGE_EXTS to module scope, add avif (absorb fix)

* fix: improve mobile touch responsiveness for session list items

iPad Safari has known issues with the click/dblclick pattern on touch:
- :hover-triggered padding-right layout shift causes the first tap click
  to target the wrong element (actions button that just appeared)
- No touch-action:manipulation means iOS still delays taps for
  double-tap zoom detection
- The old onclick+ondblclick pattern is designed for mouse, not touch

Changes:
- CSS: Remove :hover from padding-right rule to prevent layout shift
- CSS: Add touch-action:manipulation and -webkit-tap-highlight-color
  to .session-item for immediate tap response
- JS: Replace onclick/ondblclick with onpointerup + manual 350ms
  double-tap detection — works consistently on mouse and touch

* fix(nesquena#1106): iterate custom_providers[].models dict keys for dropdown population

- After reading singular 'model' field, also iterate 'models' dict keys
- Deduplicate: model field value not repeated if also in models dict
- Skip non-string keys gracefully
- Works for both named and unnamed custom_providers entries
- Add 7 regression tests

* fix(nesquena#1105): allow custom_providers hostnames through SSRF check

- Build trusted hostname set from custom_providers[].base_url in config.yaml
- These are user-explicitly configured endpoints — not SSRF risks
- Hardcoded allowlist (ollama, localhost, 127.0.0.1, lmstudio) still active
- Unknown private IPs still blocked
- Add 7 tests (5 source analysis + 2 functional with mocked socket)

* fix(tests): update hover padding assertions for nesquena#1110 touch fix (absorb)

* fix(css): restore hover padding via @media (hover:hover) for mouse devices (absorb)

* fix: filter right/middle-click from pointerup handler (absorb)

* docs: v0.50.221 release notes and version bump

---------

Co-authored-by: bergeouss <[email protected]>
Co-authored-by: nesquena-hermes <[email protected]>
Co-authored-by: sheng <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working merge soon Reviewed and queued for next release batch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants