v0.10.0 0f4e9eb

OmniboxSuggestions.svelte RawHistoryFolder

1<!--
2  Omnibox Suggestions Component
3
4  Displays search suggestions with favicons and URL highlighting
5-->
6<script lang="ts">
7  import { omniboxStore } from './stores.svelte.ts';
8  import { omniboxBridge } from './messaging';
9
10  // Reactive state
11  let suggestions = $derived(omniboxStore.suggestions);
12  let selectedIndex = $derived(omniboxStore.selectedIndex);
13  let hasContent = $state(false);
14
15  // Update hasContent when suggestions change
16  $effect(() => {
17    hasContent = omniboxStore.mode === 'omnibox' && suggestions.length > 0;
18  });
19
20  // Handle suggestion item mouse enter
21  function handleItemMouseEnter(index: number) {
22    omniboxStore.setSelectedIndex(index);
23    omniboxStore.setFaded(true);
24    scrollToSelection();
25  }
26
27  // Handle suggestion item click
28  function handleItemClick(suggestion: any) {
29    omniboxBridge.navigate(suggestion.url);
30    omniboxStore.close();
31  }
32
33  // Handle keyboard activation on suggestion item
34  function handleItemKeyDown(event: KeyboardEvent, suggestion: any) {
35    if (event.key === 'Enter' || event.key === ' ') {
36      event.preventDefault();
37      handleItemClick(suggestion);
38    }
39  }
40
41  // Scroll list to show selected item
42  function scrollToSelection() {
43    const selectedItem = document.getElementById(`omnibox-item-${selectedIndex}`);
44    if (selectedItem && selectedItem.scrollIntoView) {
45      try {
46        selectedItem.scrollIntoView({ block: 'nearest' });
47      } catch {
48        selectedItem.scrollIntoView();
49      }
50    }
51  }
52
53  // Parse URL for display
54  function parseUrl(url: string) {
55    try {
56      const urlObj = new URL(url, window.location.href);
57      return {
58        domain: urlObj.hostname,
59        path: (urlObj.pathname || '') + (urlObj.search || '') + (urlObj.hash || '')
60      };
61    } catch {
62      return {
63        domain: url || '',
64        path: ''
65      };
66    }
67  }
68
69  // Handle favicon error
70  function handleFaviconError(event: Event) {
71    const target = event.target as HTMLImageElement;
72    const chip = target.parentElement;
73    if (chip) {
74      chip.style.display = 'none';
75    }
76  }
77
78  // Watch for selection changes to scroll
79  $effect(() => {
80    if (selectedIndex >= 0) {
81      scrollToSelection();
82    }
83  });
84</script>
85
86{#if hasContent}
87  <div
88    id="omnibox-list"
89    class="mt-2 max-h-[50vh] overflow-auto border-t border-[#333]"
90    role="listbox"
91    aria-label="Search suggestions"
92  >
93    {#each suggestions as suggestion, index (suggestion.url)}
94      {@const { domain, path } = parseUrl(suggestion.url)}
95      {@const isSelected = index === selectedIndex}
96
97      <div
98        id="omnibox-item-{index}"
99        class="px-2.5 py-2 flex gap-2.5 items-center cursor-pointer
100               border-b border-[#2a2a2a] last:border-b-0
101               {isSelected ? 'bg-[#0a0a0a]' : ''}"
102        role="option"
103        tabindex={isSelected ? 0 : -1}
104        aria-selected={isSelected}
105        onmouseenter={() => handleItemMouseEnter(index)}
106        onclick={() => handleItemClick(suggestion)}
107        onkeydown={(e) => handleItemKeyDown(e, suggestion)}
108      >
109        <!-- Favicon chip -->
110        {#if suggestion.favicon}
111          <div
112            class="flex-shrink-0 w-5 h-5 rounded-full
113                   bg-[#ccc] border border-black/12
114                   shadow-sm flex items-center justify-center"
115          >
116            <img
117              src={suggestion.favicon}
118              alt=""
119              class="w-4 h-4 filter brightness-105 contrast-103"
120              loading="lazy"
121              onerror={handleFaviconError}
122              style="image-rendering: -webkit-optimize-contrast;"
123            />
124          </div>
125        {/if}
126
127        <!-- URL text with gradient fade -->
128        <div
129          class="flex-1 min-w-0 flex gap-2 items-center
130                 whitespace-nowrap overflow-hidden
131                 [mask-image:linear-gradient(90deg,black_85%,transparent_100%)]
132                 [-webkit-mask-image:linear-gradient(90deg,black_85%,transparent_100%)]"
133        >
134          <!-- Domain -->
135          <span class="text-[#e6e6e6] opacity-95">
136            {domain}
137          </span>
138
139          <!-- Separator -->
140          <span class="text-[#777]">
141            {' | '}
142          </span>
143
144          <!-- Path -->
145          <span class="text-[#99aadd]">
146            {path || '/'}
147          </span>
148        </div>
149      </div>
150    {/each}
151  </div>
152{/if}
153

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