From b5b6b4d0a9553849daa1c43af35393cf1a4c91a3 Mon Sep 17 00:00:00 2001 From: timmy Date: Sat, 8 Aug 2026 16:14:29 +0000 Subject: [PATCH] feat: make mobile work search keyboard-safe (#317) --- frontend/dashboard.css | 8 +++++ frontend/dashboard.js | 17 +++++++++- frontend/index.html | 7 +++- frontend/mobile-search-viewport.js | 50 +++++++++++++++++++++++++++ frontend/service-worker.js | 3 +- tests/test_command_palette.py | 54 ++++++++++++++++++++++++++++++ tests/test_service_worker.py | 10 +++++- 7 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 frontend/mobile-search-viewport.js diff --git a/frontend/dashboard.css b/frontend/dashboard.css index 526cda5..cdc3616 100644 --- a/frontend/dashboard.css +++ b/frontend/dashboard.css @@ -39,6 +39,8 @@ input[type=\"text\"], textarea { width: 100%; padding: 8px; border-radius: 8px; textarea { resize: vertical; min-height: 120px; } #cmd-palette { position: fixed; left: 50%; top: 10%; transform: translateX(-50%); width: min(900px, 94vw); background: rgba(11,21,38,.96); border: 1px solid #2a496e; border-radius: 12px; box-shadow: 0 20px 70px rgba(0,0,0,.55); padding: 10px; z-index: 30; display: none; backdrop-filter: blur(12px); } #cmd-palette.open { display: block; } +.cmd-palette-header { display:none; align-items:center; justify-content:space-between; gap:10px; } +#cmd-results { margin-top:8px; max-height:min(65vh,520px); overflow-y:auto; } .cmd-item { padding: 10px; min-height:44px; cursor: pointer; border-radius: 10px; color:#e5e7eb; display:flex; gap:10px; align-items:center; justify-content:space-between; } .cmd-item:hover, .cmd-item.selected { background: #10233a; outline:1px solid #31577f; } .cmd-meta { color:#93a4b8; font-size:12px; text-align:right; } @@ -300,6 +302,12 @@ textarea { resize: vertical; min-height: 120px; } .update-sheet-panel { width:100%; border-left:0; padding:14px; } .issue-sheet-panel { width:100%; border-left:0; padding:14px; } .search-preview-panel { width:100%; border-left:0; padding:14px; padding-bottom:calc(14px + env(safe-area-inset-bottom)); overflow-x:hidden; } + #cmd-palette { left:0; top:var(--search-viewport-top,0px); transform:none; width:100%; height:var(--search-viewport-height,100dvh); border:0; border-radius:0; padding:12px; padding-bottom:calc(12px + env(safe-area-inset-bottom)); z-index:46; } + #cmd-palette.open { display:flex; flex-direction:column; } + .cmd-palette-header { display:flex; flex:0 0 auto; min-height:44px; } + #close-command-palette { min-height:44px; } + #cmd-input { flex:0 0 auto; min-height:44px; } + #cmd-results { flex:1; min-height:0; overflow-y:auto; max-height:none; overscroll-behavior:contain; padding-bottom:env(safe-area-inset-bottom); } .create-issue-panel { width:100%; border-left:0; padding:14px; } .pull-sheet-panel { width:100%; border-left:0; padding:14px; } .mobile-task-dock { position:fixed; inset:auto 0 0; z-index:45; display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:2px; padding:6px 8px; padding-bottom:env(safe-area-inset-bottom); border-top:1px solid #2a496e; background:rgba(11,21,38,.98); backdrop-filter:blur(12px); } diff --git a/frontend/dashboard.js b/frontend/dashboard.js index f569ccf..9018b28 100644 --- a/frontend/dashboard.js +++ b/frontend/dashboard.js @@ -1982,6 +1982,13 @@ ), onState: renderSearchPreview, }); + const mobileSearchViewport = createMobileSearchViewport({ + palette: qs('#cmd-palette'), + results: qs('#cmd-results'), + viewport: window.visualViewport, + mediaQuery: window.matchMedia('(max-width: 600px)'), + schedule: callback => requestAnimationFrame(callback), + }); function closeSearchPreview(navigate = true) { if (navigate && taskOverlayHistory.current() === 'search-preview') { taskOverlayHistory.close(); @@ -2009,6 +2016,7 @@ item.command.run(); qs('#cmd-input').value = ''; } else { + mobileSearchViewport.rememberScroll(); searchPreview.open(item.result).catch(() => {}); taskOverlayHistory.open('search-preview'); } @@ -2045,6 +2053,8 @@ return; } qs('#cmd-palette').classList.add('open'); + mobileSearchViewport.open(); + mobileSearchViewport.restoreScroll(); qs('#cmd-input').setAttribute('aria-expanded', 'true'); qs('#cmd-input').focus(); commandSelection = -1; @@ -2062,11 +2072,15 @@ if (previous === 'find' && kind !== 'find') closeFindWorkSheet(false); if (previous === 'search-preview' && kind !== 'search-preview') { if (kind === 'search') closeSearchPreview(false); - else searchPreview.close(); + else { + searchPreview.close(); + mobileSearchViewport.close(); + } } if (previous === 'search' && kind !== 'search' && kind !== 'search-preview') { qs('#cmd-palette').classList.remove('open'); qs('#cmd-input').setAttribute('aria-expanded', 'false'); + mobileSearchViewport.close(); qs('#open-palette').focus(); } if (kind === 'new' && previous !== 'new') openCreateIssueSheet(false); @@ -2076,6 +2090,7 @@ }); taskOverlayHistory.start(); qs('#open-palette').addEventListener('click', openCommandPalette); + qs('#close-command-palette').addEventListener('click', () => taskOverlayHistory.close()); qs('#cmd-input').addEventListener('input', (e) => { commandSelection = -1; renderCommands(e.target.value); diff --git a/frontend/index.html b/frontend/index.html index 8bbb040..76d0191 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -153,8 +153,12 @@