various fixes, suggestion improvements

This commit is contained in:
cade
2026-03-21 00:40:12 +00:00
parent 3b17c68a04
commit f75809d660
+49 -22
View File
@@ -11,6 +11,7 @@
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
src: url('./fonts/SpaceGrotesk-Regular.woff2') format('woff2'); src: url('./fonts/SpaceGrotesk-Regular.woff2') format('woff2');
font-display: swap;
} }
@font-face { @font-face {
@@ -18,6 +19,7 @@
font-style: normal; font-style: normal;
font-weight: bold; font-weight: bold;
src: url('./fonts/SpaceGrotesk-Bold.woff2') format('woff2'); src: url('./fonts/SpaceGrotesk-Bold.woff2') format('woff2');
font-display: swap;
} }
:root { :root {
@@ -74,7 +76,7 @@
['t', { name: 'TickTick', url: 'https://ticktick.com/webapp/#q/today/tasks' }], ['t', { name: 'TickTick', url: 'https://ticktick.com/webapp/#q/today/tasks' }],
['u', { name: 'Duolingo', url: 'https://www.duolingo.com/learn' }], ['u', { name: 'Duolingo', url: 'https://www.duolingo.com/learn' }],
['v', { name: 'Vercel', url: 'https://vercel.com/dashboard' }], ['v', { name: 'Vercel', url: 'https://vercel.com/dashboard' }],
['x', { name: '𝕏', suggestions: ['x/dev', 'x/seerr', 'x/jellyfin', 'x/radarr', 'x/sonarr', 'x/prowlarr', 'x/bazarr', 'x/profilarr', 'x/nzbget', 'x/n8n', 'x/nginx', 'x/wg'], url: 'https://x.com/home' }], ['x', { name: '𝕏', suggestions: ['x/dev', 'x/n8n', 'x/nginx', 'x/wg'], url: 'https://x.com/home' }],
['x/bazarr', { url: 'https://bazarr.xvyz.co' }], ['x/bazarr', { url: 'https://bazarr.xvyz.co' }],
['x/dev', { url: 'https://dev.xvyz.co' }], ['x/dev', { url: 'https://dev.xvyz.co' }],
['x/jellyfin', { url: 'https://jellyfin.xvyz.co' }], ['x/jellyfin', { url: 'https://jellyfin.xvyz.co' }],
@@ -151,7 +153,7 @@
@media (min-width: 60rem) { @media (min-width: 60rem) {
.commands { .commands {
grid-template-columns: repeat(11, 1fr);h grid-template-columns: repeat(11, 1fr);
} }
} }
</style> </style>
@@ -162,8 +164,8 @@
<template id="command-template"> <template id="command-template">
<li> <li>
<a class="command" rel="noopener noreferrer"> <a class="command" rel="noopener noreferrer" aria-label="">
<span class="key"></span> <span class="key" aria-hidden="true"></span>
<span class="name"></span> <span class="name"></span>
</a> </a>
</li> </li>
@@ -184,6 +186,7 @@
const clone = commandTemplate.content.cloneNode(true); const clone = commandTemplate.content.cloneNode(true);
const command = clone.querySelector('.command'); const command = clone.querySelector('.command');
command.href = url; command.href = url;
command.setAttribute('aria-label', name);
if (CONFIG.openLinksInNewTab) command.target = '_blank'; if (CONFIG.openLinksInNewTab) command.target = '_blank';
clone.querySelector('.key').innerText = key; clone.querySelector('.key').innerText = key;
clone.querySelector('.name').innerText = name; clone.querySelector('.name').innerText = name;
@@ -232,6 +235,16 @@
width: 100%; width: 100%;
} }
.sr-only {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
.input { .input {
color: var(--color-text); color: var(--color-text);
font-size: 2rem; font-size: 2rem;
@@ -300,9 +313,10 @@
} }
} }
</style> </style>
<dialog class="dialog"> <dialog class="dialog" aria-label="Search">
<form autocomplete="off" class="form" method="dialog" spellcheck="false"> <form autocomplete="off" class="form" method="dialog" spellcheck="false">
<input class="input" title="search" type="text" /> <label for="search-input" class="sr-only">Search</label>
<input class="input" id="search-input" type="text" />
<menu class="suggestions"></menu> <menu class="suggestions"></menu>
</form> </form>
</dialog> </dialog>
@@ -362,6 +376,11 @@
document.querySelector('head').appendChild(script); document.querySelector('head').appendChild(script);
script.src = `https://duckduckgo.com/ac/?callback=autocompleteCallback&q=${search}`; script.src = `https://duckduckgo.com/ac/?callback=autocompleteCallback&q=${search}`;
script.onload = script.remove; script.onload = script.remove;
script.onerror = () => {
script.remove();
resolve([]);
};
}); });
} }
@@ -386,8 +405,8 @@
} }
if (COMMANDS.has(query)) { if (COMMANDS.has(query)) {
const { key, url } = COMMANDS.get(query); const { url } = COMMANDS.get(query);
return { key, query, url }; return { key: query, query, url };
} }
let splitBy = CONFIG.commandSearchDelimiter; let splitBy = CONFIG.commandSearchDelimiter;
@@ -452,6 +471,7 @@
} }
const parentKey = oq.query.split(CONFIG.commandPathDelimiter)[0]; const parentKey = oq.query.split(CONFIG.commandPathDelimiter)[0];
const exactMatch = COMMANDS.has(oq.query);
let suggestions = [ let suggestions = [
...(COMMANDS.get(oq.query)?.suggestions ?? ...(COMMANDS.get(oq.query)?.suggestions ??
@@ -459,26 +479,33 @@
[]), []),
]; ];
for (const key of COMMANDS.keys()) { if (exactMatch) {
if ( suggestions = suggestions.filter((s) => s.startsWith(oq.query));
key.startsWith(oq.query) && } else {
key !== oq.query && for (const key of COMMANDS.keys()) {
!suggestions.includes(key) if (key !== oq.query && !suggestions.includes(key)) {
) { if (key.startsWith(oq.query) || key.includes(oq.query)) {
suggestions.push(key); suggestions.push(key);
}
}
} }
suggestions = suggestions.filter(
(s) => s.startsWith(oq.query) || s.includes(oq.query),
);
} }
suggestions = suggestions.filter((s) => s.startsWith(oq.query)); if (oq.search?.length > 1 && !exactMatch) {
if (oq.search?.length > 1 && suggestions.length === 0) {
const res = await Search.#fetchDuckDuckGoSuggestions(oq.search); const res = await Search.#fetchDuckDuckGoSuggestions(oq.search);
suggestions = oq.splitBy const ddgSuggestions = oq.splitBy
? res.map((search) => `${oq.key}${oq.splitBy}${search}`) ? res.map((search) => `${oq.key}${oq.splitBy}${search}`)
: res; : res;
suggestions = suggestions.slice(0, CONFIG.suggestionLimit); suggestions = [...suggestions, ...ddgSuggestions].slice(
0,
CONFIG.suggestionLimit,
);
} }
const nq = Search.#parseQuery(this.#input.value); const nq = Search.#parseQuery(this.#input.value);
@@ -552,8 +579,8 @@
const pre = suggestion.slice(0, matched.index); const pre = suggestion.slice(0, matched.index);
const post = suggestion.slice(matched.index + matched[0].length); const post = suggestion.slice(matched.index + matched[0].length);
matchRef.innerText = matched[0]; matchRef.innerText = matched[0];
matchRef.insertAdjacentHTML('beforebegin', pre); matchRef.insertAdjacentText('beforebegin', pre);
matchRef.insertAdjacentHTML('afterend', post); matchRef.insertAdjacentText('afterend', post);
ref.append(clone); ref.append(clone);
} else { } else {
ref.innerText = suggestion; ref.innerText = suggestion;