Skip to content

Commit 144c45b

Browse files
committed
[frontend] autocomplete: pull entries from global history
1 parent d269c71 commit 144c45b

File tree

1 file changed

+96
-19
lines changed

1 file changed

+96
-19
lines changed

frontend/src/components/Omnibox.tsx

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ import { IconButton } from "./IconButton";
1313
import { createDelegate, type Delegate } from "dreamland/core";
1414
import type { Tab } from "../Tab";
1515

16+
export function trimUrl(v: URL) {
17+
return (
18+
(v.protocol === "puter:" ? v.protocol : "") + v.host + v.pathname + v.search
19+
);
20+
}
21+
1622
export const Spacer: Component = function (cx) {
1723
return <div></div>;
1824
};
@@ -22,6 +28,13 @@ Spacer.style = css`
2228
}
2329
`;
2430

31+
type OmniboxResult = {
32+
kind: "search" | "history" | "bookmark" | "direct";
33+
title?: string;
34+
url: string;
35+
favicon?: string | null;
36+
};
37+
2538
export const UrlInput: Component<
2639
{
2740
tabUrl: URL;
@@ -34,21 +47,66 @@ export const UrlInput: Component<
3447

3548
focusindex: number;
3649

37-
overflowItems: string[];
50+
overflowItems: OmniboxResult[];
3851
}
3952
> = function (cx) {
4053
this.focusindex = 0;
4154
this.overflowItems = [];
4255
this.value = "";
4356
const fetchSuggestions = async () => {
57+
let search = this.input.value;
58+
59+
this.overflowItems = [];
60+
61+
for (const entry of browser.globalhistory) {
62+
if (!entry.url.href.includes(search) && !entry.title?.includes(search))
63+
continue;
64+
if (this.overflowItems.some((i) => i.url === entry.url.href)) continue;
65+
66+
this.overflowItems.push({
67+
kind: "history",
68+
title: entry.title,
69+
url: entry.url.href,
70+
favicon: entry.favicon,
71+
});
72+
}
73+
this.overflowItems = this.overflowItems.slice(0, 5);
74+
75+
if (
76+
search.startsWith("http:") ||
77+
search.startsWith("https:") ||
78+
search.startsWith("puter:")
79+
) {
80+
this.overflowItems = [
81+
{
82+
kind: "direct",
83+
url: search,
84+
},
85+
...this.overflowItems,
86+
];
87+
return;
88+
}
89+
4490
let resp = await fetch(
4591
scramjet.encodeUrl(
46-
`http://suggestqueries.google.com/complete/search?client=chrome&q=${encodeURIComponent(this.input.value)}`
92+
`http://suggestqueries.google.com/complete/search?client=chrome&q=${encodeURIComponent(search)}`
4793
)
4894
);
4995
let json = await resp.json();
50-
console.log(json);
51-
this.overflowItems = json[1].slice(0, 5);
96+
for (const item of json[1].slice(0, 5)) {
97+
// it's gonna be stuff like "http //fortnite.com/2fa ps5"
98+
// these results are generally useless
99+
if (item.startsWith("http")) continue;
100+
101+
this.overflowItems.push({
102+
kind: "search",
103+
title: item,
104+
url: `https://www.google.com/search?q=${encodeURIComponent(item)}`,
105+
favicon: scramjet.encodeUrl("https://www.google.com/favicon.ico"),
106+
});
107+
}
108+
109+
this.overflowItems = this.overflowItems;
52110
};
53111
let currentTimeout: number | null = null;
54112
let ratelimiting = false;
@@ -110,18 +168,23 @@ export const UrlInput: Component<
110168
<div
111169
class="overflowitem"
112170
on:click={() => {
113-
this.value = item;
114171
this.active = false;
115172
this.input.blur();
116173

117-
browser.searchNavigate(this.value);
174+
browser.activetab.pushNavigate(new URL(item.url));
118175
}}
119176
class:focused={use(this.focusindex).map(
120177
(i) => i - 1 === this.overflowItems.indexOf(item)
121178
)}
122179
>
123-
<IconButton icon={iconSearch}></IconButton>
124-
<span>{item}</span>
180+
<img
181+
class="favicon"
182+
src={item.favicon || "/vite.svg"}
183+
alt="favicon"
184+
/>
185+
{(item.title && <span class="description">{item.title} - </span>) ||
186+
""}
187+
<span class="url">{item.url}</span>
125188
</div>
126189
))}
127190
</div>
@@ -149,8 +212,10 @@ export const UrlInput: Component<
149212
if (e.key === "Enter") {
150213
e.preventDefault();
151214
if (this.focusindex > 0) {
152-
this.value = this.overflowItems[this.focusindex - 1];
153-
this.navigate(this.value);
215+
// this.value = this.overflowItems[this.focusindex - 1].;
216+
browser.activetab.pushNavigate(
217+
new URL(this.overflowItems[this.focusindex - 1].url)
218+
);
154219
this.active = false;
155220
this.input.blur();
156221
} else {
@@ -167,15 +232,7 @@ export const UrlInput: Component<
167232
{use(this.active)
168233
.map((a) => !a)
169234
.andThen(
170-
<span class="inactiveurl">
171-
{use(this.tabUrl).map(
172-
(v) =>
173-
(v.protocol === "puter:" ? v.protocol : "") +
174-
v.host +
175-
v.pathname +
176-
v.search
177-
)}
178-
</span>
235+
<span class="inactiveurl">{use(this.tabUrl).map(trimUrl)}</span>
179236
)}
180237

181238
<IconButton icon={iconStar}></IconButton>
@@ -190,6 +247,12 @@ UrlInput.style = css`
190247
display: flex;
191248
height: 100%;
192249
}
250+
251+
.favicon {
252+
width: 16px;
253+
height: 16px;
254+
}
255+
193256
.overflow {
194257
position: absolute;
195258
display: none;
@@ -207,6 +270,20 @@ UrlInput.style = css`
207270
align-items: center;
208271
height: 2.5em;
209272
cursor: pointer;
273+
gap: 0.5em;
274+
padding-left: 0.5em;
275+
white-space: nowrap;
276+
}
277+
.overflowitem .url,
278+
.overflowitem .description {
279+
text-overflow: ellipsis;
280+
text-wrap: nowrap;
281+
word-wrap: nowrap;
282+
overflow: hidden;
283+
}
284+
285+
.overflowitem .url {
286+
color: grey;
210287
}
211288
.overflowitem.focused {
212289
background: blue;

0 commit comments

Comments
 (0)