v0.14.0 76d1892

OmniboxFind.svelte RawHistoryFolder

1<!--
2  Omnibox Find Component
3
4  Displays find results and match navigation
5-->
6<script lang="ts">
7  import { omniboxStore } from './stores.svelte.ts';
8  import { revealMatch } from './find';
9
10  // Reactive state
11  let matches = $derived(omniboxStore.matches);
12  let selectedIndex = $derived(omniboxStore.selectedIndex);
13  let hasContent = $state(false);
14
15  // Update hasContent when matches change
16  $effect(() => {
17    hasContent = omniboxStore.mode === 'find' && matches.length > 0;
18  });
19
20  // Computed total count
21  let totalMatches = $derived(matches.length);
22
23  // Handle match item mouse enter
24  function handleItemMouseEnter(index: number) {
25    omniboxStore.setSelectedIndex(index);
26    omniboxStore.setFaded(true);
27    scrollToSelection();
28  }
29
30  // Handle match item click
31  function handleItemClick(index: number) {
32    omniboxStore.setSelectedIndex(index);
33    revealMatch(index);
34    omniboxStore.close();
35  }
36
37  // Scroll list to show selected item
38  function scrollToSelection() {
39    const selectedItem = document.getElementById(`find-item-${selectedIndex}`);
40    if (selectedItem && selectedItem.scrollIntoView) {
41      try {
42        selectedItem.scrollIntoView({ block: 'nearest' });
43      } catch {
44        selectedItem.scrollIntoView();
45      }
46    }
47  }
48
49  // Watch for selection changes to scroll
50  $effect(() => {
51    if (selectedIndex >= 0) {
52      scrollToSelection();
53    }
54  });
55</script>
56
57<div id="find-list" class="find-list" role="listbox" aria-label="Find results">
58  <!-- Header with match count -->
59  <div class="find-header">
60    {#if totalMatches > 0}
61      {totalMatches} match{totalMatches === 1 ? '' : 'es'}
62    {:else}
63      No matches
64    {/if}
65  </div>
66
67  <!-- Match results -->
68  {#if hasContent}
69    {#each matches as match, index (index)}
70      {@const isSelected = index === selectedIndex}
71
72      <div
73        id="find-item-{index}"
74        class={isSelected ? 'find-item selected' : 'find-item'}
75        role="option"
76        tabindex="-1"
77        aria-selected={isSelected}
78        onmouseenter={() => handleItemMouseEnter(index)}
79        onclick={() => handleItemClick(index)}
80        onkeydown={(e) => e.key === 'Enter' && handleItemClick(index)}
81      >
82        <!-- Match context -->
83        <div class="find-context">
84          {match.context || ''}
85        </div>
86      </div>
87    {/each}
88  {/if}
89</div>
90
91<style>
92  .find-list {
93    margin-top: 0.75rem;
94    max-height: 50vh;
95    overflow-y: auto;
96    border-top: 1px solid var(--dynamic-border);
97    background: color-mix(in srgb, var(--dynamic-bg) 94%, var(--dynamic-surface) 6%);
98  }
99
100  .find-header {
101    padding: 0.65rem 0.85rem;
102    font-size: 0.7rem;
103    text-transform: uppercase;
104    letter-spacing: 0.08em;
105    color: var(--dynamic-muted);
106    border-bottom: 1px dashed var(--dynamic-border);
107  }
108
109  .find-item {
110    padding: 0.65rem 0.85rem;
111    border-bottom: 1px dashed var(--dynamic-border);
112    cursor: pointer;
113    transition: background-color 120ms ease, color 120ms ease;
114  }
115
116  .find-item:last-child {
117    border-bottom: none;
118  }
119
120  .find-item.selected,
121  .find-item:hover,
122  .find-item:focus-visible {
123    background: color-mix(in srgb, var(--dynamic-bg) 75%, var(--dynamic-surface) 25%);
124    color: var(--dynamic-text);
125    outline: none;
126  }
127
128  .find-context {
129    color: var(--dynamic-text);
130    font-size: 0.75rem;
131    white-space: nowrap;
132    overflow: hidden;
133    text-overflow: ellipsis;
134  }
135</style>

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