detached 06fdee3

fix(gui): strengthen omnibox positioning with explicit fixed positioning and responsive styles

commit
06fdee3c523ab1c53eb3c41c91f4ad38dcb2038a
author
bamen · · 2025-09-15 08:26 UTC
parent
61f8c29
signature
unsigned
2 files · +65 -17
M gui/src/components/omnibox/Omnibox.svelte +55 -8
@@ -20,6 +20,14 @@
2020 let boxElement: HTMLDivElement;
2121 let inputElement = $state<HTMLInputElement>();
2222
23+ // Responsive styling state
24+ let responsiveStyles = $state({
25+ width: 'min(90vw, 720px)',
26+ padding: '8px 12px',
27+ fontSize: '16px',
28+ inputPadding: '10px 12px'
29+ });
30+
2331 // Handle click outside to close
2432 function handleOverlayClick(event: MouseEvent) {
2533 const target = event.target as HTMLElement;
@@ -47,6 +55,20 @@
4755 }
4856 }
4957
58+ // Update responsive styles based on viewport width (handles zoom levels)
59+ function updateResponsiveStyles() {
60+ const vw = window.innerWidth;
61+ const isSmall = vw < 640;
62+ const isLarge = vw > 1920;
63+
64+ responsiveStyles = {
65+ width: isSmall ? '95vw' : isLarge ? 'min(90vw, 840px)' : 'min(90vw, 720px)',
66+ padding: isSmall ? '6px 8px' : '8px 12px',
67+ fontSize: isSmall ? '14px' : isLarge ? '17px' : '16px',
68+ inputPadding: isSmall ? '8px 10px' : '10px 12px'
69+ };
70+ }
71+
5072 // Focus input when component becomes visible
5173 $effect(() => {
5274 if (visible && inputElement) {
@@ -92,6 +114,12 @@
92114 onMount(() => {
93115 console.log('🔧 Omnibox component mounted');
94116
117+ // Initialize responsive styles
118+ updateResponsiveStyles();
119+
120+ // Add resize listener for responsive updates (handles zoom changes)
121+ window.addEventListener('resize', updateResponsiveStyles);
122+
95123 // Add global click listener
96124 document.addEventListener('click', handleGlobalClick, true);
97125
@@ -156,6 +184,9 @@
156184 });
157185
158186 onDestroy(() => {
187+ // Cleanup resize listener
188+ window.removeEventListener('resize', updateResponsiveStyles);
189+
159190 // Cleanup global click listener
160191 document.removeEventListener('click', handleGlobalClick, true);
161192
@@ -172,19 +203,35 @@
172203 <!-- Overlay container -->
173204 <div
174205 class="fixed inset-0 z-[2147483647]"
175- style="display: {visible ? 'block' : 'none'}"
206+ style="display: {visible ? 'block' : 'none'};
207+ position: fixed !important;
208+ top: 0 !important;
209+ left: 0 !important;
210+ right: 0 !important;
211+ bottom: 0 !important;
212+ z-index: 2147483647 !important;
213+ pointer-events: none !important;
214+ margin: 0 !important;
215+ padding: 0 !important;
216+ isolation: isolate;"
176217 onmousedown={handleOverlayClick}
177218 role="presentation"
178219 >
179220 <!-- Main omnibox container -->
180221 <div
181222 bind:this={boxElement}
182- class="w-[min(90vw,720px)] mx-auto mt-[8vh] p-3
183- bg-[#1b1b1b] border border-[#444] rounded-lg shadow-[0_10px_30px_rgba(0,0,0,0.6)]
184- text-[#eee]
185- sm:w-[95vw] sm:p-2
186- xl:w-[min(90vw,840px)]"
187- style="font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', Arial, sans-serif"
223+ class="bg-[#1b1b1b] border border-[#444] rounded-lg shadow-[0_10px_30px_rgba(0,0,0,0.6)] text-[#eee]"
224+ style="position: relative !important;
225+ left: 50% !important;
226+ transform: translateX(-50%) !important;
227+ margin-top: 8vh !important;
228+ margin-left: 0 !important;
229+ margin-right: 0 !important;
230+ width: {responsiveStyles.width};
231+ padding: {responsiveStyles.padding};
232+ font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', Arial, sans-serif;
233+ pointer-events: auto !important;
234+ box-sizing: border-box !important;"
188235 onmousedown={handleBoxClick}
189236 onmouseenter={handleMouseEnter}
190237 role="dialog"
@@ -193,7 +240,7 @@
193240 aria-label={mode === 'find' ? 'Find in page' : 'Omnibox search'}
194241 >
195242 <!-- Input component -->
196- <OmniboxInput bind:inputElement />
243+ <OmniboxInput bind:inputElement {responsiveStyles} />
197244
198245 <!-- Content based on mode -->
199246 {#if mode === 'omnibox'}
@@ -20,6 +20,14 @@
20 let boxElement: HTMLDivElement;20 let boxElement: HTMLDivElement;
21 let inputElement = $state<HTMLInputElement>();21 let inputElement = $state<HTMLInputElement>();
22 22
23+ // Responsive styling state
24+ let responsiveStyles = $state({
25+ width: 'min(90vw, 720px)',
26+ padding: '8px 12px',
27+ fontSize: '16px',
28+ inputPadding: '10px 12px'
29+ });
30+
23 // Handle click outside to close31 // Handle click outside to close
24 function handleOverlayClick(event: MouseEvent) {32 function handleOverlayClick(event: MouseEvent) {
25 const target = event.target as HTMLElement;33 const target = event.target as HTMLElement;
@@ -47,6 +55,20 @@
47 }55 }
48 }56 }
49 57
58+ // Update responsive styles based on viewport width (handles zoom levels)
59+ function updateResponsiveStyles() {
60+ const vw = window.innerWidth;
61+ const isSmall = vw < 640;
62+ const isLarge = vw > 1920;
63+
64+ responsiveStyles = {
65+ width: isSmall ? '95vw' : isLarge ? 'min(90vw, 840px)' : 'min(90vw, 720px)',
66+ padding: isSmall ? '6px 8px' : '8px 12px',
67+ fontSize: isSmall ? '14px' : isLarge ? '17px' : '16px',
68+ inputPadding: isSmall ? '8px 10px' : '10px 12px'
69+ };
70+ }
71+
50 // Focus input when component becomes visible72 // Focus input when component becomes visible
51 $effect(() => {73 $effect(() => {
52 if (visible && inputElement) {74 if (visible && inputElement) {
@@ -92,6 +114,12 @@
92 onMount(() => {114 onMount(() => {
93 console.log('🔧 Omnibox component mounted');115 console.log('🔧 Omnibox component mounted');
94 116
117+ // Initialize responsive styles
118+ updateResponsiveStyles();
119+
120+ // Add resize listener for responsive updates (handles zoom changes)
121+ window.addEventListener('resize', updateResponsiveStyles);
122+
95 // Add global click listener123 // Add global click listener
96 document.addEventListener('click', handleGlobalClick, true);124 document.addEventListener('click', handleGlobalClick, true);
97 125
@@ -156,6 +184,9 @@
156 });184 });
157 185
158 onDestroy(() => {186 onDestroy(() => {
187+ // Cleanup resize listener
188+ window.removeEventListener('resize', updateResponsiveStyles);
189+
159 // Cleanup global click listener190 // Cleanup global click listener
160 document.removeEventListener('click', handleGlobalClick, true);191 document.removeEventListener('click', handleGlobalClick, true);
161 192
@@ -172,19 +203,35 @@
172 <!-- Overlay container -->203 <!-- Overlay container -->
173 <div204 <div
174 class="fixed inset-0 z-[2147483647]"205 class="fixed inset-0 z-[2147483647]"
175- style="display: {visible ? 'block' : 'none'}"206+ style="display: {visible ? 'block' : 'none'};
207+ position: fixed !important;
208+ top: 0 !important;
209+ left: 0 !important;
210+ right: 0 !important;
211+ bottom: 0 !important;
212+ z-index: 2147483647 !important;
213+ pointer-events: none !important;
214+ margin: 0 !important;
215+ padding: 0 !important;
216+ isolation: isolate;"
176 onmousedown={handleOverlayClick}217 onmousedown={handleOverlayClick}
177 role="presentation"218 role="presentation"
178 >219 >
179 <!-- Main omnibox container -->220 <!-- Main omnibox container -->
180 <div221 <div
181 bind:this={boxElement}222 bind:this={boxElement}
182- class="w-[min(90vw,720px)] mx-auto mt-[8vh] p-3223+ class="bg-[#1b1b1b] border border-[#444] rounded-lg shadow-[0_10px_30px_rgba(0,0,0,0.6)] text-[#eee]"
183- bg-[#1b1b1b] border border-[#444] rounded-lg shadow-[0_10px_30px_rgba(0,0,0,0.6)]224+ style="position: relative !important;
184- text-[#eee]225+ left: 50% !important;
185- sm:w-[95vw] sm:p-2226+ transform: translateX(-50%) !important;
186- xl:w-[min(90vw,840px)]"227+ margin-top: 8vh !important;
187- style="font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', Arial, sans-serif"228+ margin-left: 0 !important;
229+ margin-right: 0 !important;
230+ width: {responsiveStyles.width};
231+ padding: {responsiveStyles.padding};
232+ font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', Arial, sans-serif;
233+ pointer-events: auto !important;
234+ box-sizing: border-box !important;"
188 onmousedown={handleBoxClick}235 onmousedown={handleBoxClick}
189 onmouseenter={handleMouseEnter}236 onmouseenter={handleMouseEnter}
190 role="dialog"237 role="dialog"
@@ -193,7 +240,7 @@
193 aria-label={mode === 'find' ? 'Find in page' : 'Omnibox search'}240 aria-label={mode === 'find' ? 'Find in page' : 'Omnibox search'}
194 >241 >
195 <!-- Input component -->242 <!-- Input component -->
196- <OmniboxInput bind:inputElement />243+ <OmniboxInput bind:inputElement {responsiveStyles} />
197 244
198 <!-- Content based on mode -->245 <!-- Content based on mode -->
199 {#if mode === 'omnibox'}246 {#if mode === 'omnibox'}
M gui/src/components/omnibox/OmniboxInput.svelte +10 -9
@@ -15,12 +15,16 @@
1515 let suggestions = $derived(omniboxStore.suggestions);
1616 let matches = $derived(omniboxStore.matches);
1717
18- // Input element ref (props for parent to bind)
18+ // Input element ref and responsive styles (props for parent to bind)
1919 interface Props {
2020 inputElement?: HTMLInputElement;
21+ responsiveStyles?: {
22+ fontSize: string;
23+ inputPadding: string;
24+ };
2125 }
2226
23- let { inputElement = $bindable() }: Props = $props();
27+ let { inputElement = $bindable(), responsiveStyles }: Props = $props();
2428
2529 // Computed placeholder
2630 let placeholder = $derived(
@@ -185,13 +189,10 @@
185189 type="text"
186190 {placeholder}
187191 value={inputValue}
188- class="w-full box-border px-3 py-2.5
189- bg-[#121212] text-[#eee]
190- border border-[#555] rounded-md
191- focus:outline-none
192- text-base
193- sm:px-2.5 sm:py-2 sm:text-sm
194- xl:text-lg xl:px-3 xl:py-3"
192+ class="w-full box-border bg-[#121212] text-[#eee] border border-[#555] rounded-md focus:outline-none"
193+ style="padding: {responsiveStyles?.inputPadding || '10px 12px'};
194+ font-size: {responsiveStyles?.fontSize || '16px'};
195+ box-sizing: border-box"
195196 oninput={handleInput}
196197 onkeydown={handleKeyDown}
197198 onmousedown={handleMouseDown}
@@ -15,12 +15,16 @@
15 let suggestions = $derived(omniboxStore.suggestions);15 let suggestions = $derived(omniboxStore.suggestions);
16 let matches = $derived(omniboxStore.matches);16 let matches = $derived(omniboxStore.matches);
17 17
18- // Input element ref (props for parent to bind)18+ // Input element ref and responsive styles (props for parent to bind)
19 interface Props {19 interface Props {
20 inputElement?: HTMLInputElement;20 inputElement?: HTMLInputElement;
21+ responsiveStyles?: {
22+ fontSize: string;
23+ inputPadding: string;
24+ };
21 }25 }
22 26
23- let { inputElement = $bindable() }: Props = $props();27+ let { inputElement = $bindable(), responsiveStyles }: Props = $props();
24 28
25 // Computed placeholder29 // Computed placeholder
26 let placeholder = $derived(30 let placeholder = $derived(
@@ -185,13 +189,10 @@
185 type="text"189 type="text"
186 {placeholder}190 {placeholder}
187 value={inputValue}191 value={inputValue}
188- class="w-full box-border px-3 py-2.5192+ class="w-full box-border bg-[#121212] text-[#eee] border border-[#555] rounded-md focus:outline-none"
189- bg-[#121212] text-[#eee]193+ style="padding: {responsiveStyles?.inputPadding || '10px 12px'};
190- border border-[#555] rounded-md194+ font-size: {responsiveStyles?.fontSize || '16px'};
191- focus:outline-none195+ box-sizing: border-box"
192- text-base
193- sm:px-2.5 sm:py-2 sm:text-sm
194- xl:text-lg xl:px-3 xl:py-3"
195 oninput={handleInput}196 oninput={handleInput}
196 onkeydown={handleKeyDown}197 onkeydown={handleKeyDown}
197 onmousedown={handleMouseDown}198 onmousedown={handleMouseDown}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close