search improvements

This commit is contained in:
Guy Sandler 2026-10-25 16:48:01 -07:00
parent 6b06321687
commit 7ff57cd071
13 changed files with 51 additions and 13 deletions

View File

@ -1416,7 +1416,7 @@
"message": "Funktionen suchen..." "message": "Funktionen suchen..."
}, },
"search_hint": { "search_hint": {
"message": "Beginne zu tippen, um deine Module und Aufgaben zu durchsuchen. Verwende @kursname, um einen bestimmten Kurs zu durchsuchen, oder @grades, um die Notenseite eines Kurses zu öffnen, oder @home, um zu deinem Dashboard zu springen." "message": "Tippe, um deine Module und Aufgaben zu durchsuchen.\n@coursename (z. B. @chemistry chapter 5 test) sucht einen bestimmten Kurs oder seine Startseite\n@grades öffnet die Notenseite eines Kurses\n@home springt zu deinem Dashboard"
}, },
"search_navigate": { "search_navigate": {
"message": "navigieren" "message": "navigieren"

View File

@ -1440,7 +1440,7 @@
"message": "Search features..." "message": "Search features..."
}, },
"search_hint": { "search_hint": {
"message": "Start typing to search your modules and assignments. Use @coursename to search a specific course, or @grades to open a course's grades page, or @home to jump to your dashboard." "message": "Start typing to search your modules and assignments.\n@coursename (like @chemistry chapter 5 test) to search a specific course or its homepage\n@grades to open a course's grades page\n@home to jump to your dashboard"
}, },
"search_index_count": { "search_index_count": {
"message": "{{COUNT}} items" "message": "{{COUNT}} items"

View File

@ -1416,7 +1416,7 @@
"message": "Buscar funciones..." "message": "Buscar funciones..."
}, },
"search_hint": { "search_hint": {
"message": "Empieza a escribir para buscar en tus módulos y tareas. Usa @coursename para buscar un curso concreto, o @grades para abrir la página de notas de un curso, o @home para ir a tu panel." "message": "Empieza a escribir para buscar en tus módulos y tareas.\n@coursename (como @chemistry chapter 5 test) busca un curso concreto o su página principal\n@grades abre la página de calificaciones de un curso\n@home va a tu panel"
}, },
"search_navigate": { "search_navigate": {
"message": "navegar" "message": "navegar"

View File

@ -1416,7 +1416,7 @@
"message": "Rechercher des fonctionnalités..." "message": "Rechercher des fonctionnalités..."
}, },
"search_hint": { "search_hint": {
"message": "Commencez à taper pour rechercher vos modules et devoirs. Utilisez @nomducours pour chercher un cours précis, ou @grades pour ouvrir la page des notes d'un cours, ou @home pour aller à votre tableau de bord." "message": "Commencez à taper pour chercher dans vos modules et devoirs.\n@coursename (comme @chemistry chapter 5 test) cherche un cours précis ou sa page d'accueil\n@grades ouvre la page des notes d'un cours\n@home vous amène à votre tableau de bord"
}, },
"search_navigate": { "search_navigate": {
"message": "naviguer" "message": "naviguer"

View File

@ -1416,7 +1416,7 @@
"message": "Cerca funzioni..." "message": "Cerca funzioni..."
}, },
"search_hint": { "search_hint": {
"message": "Inizia a digitare per cercare moduli e consegne. Usa @corsonome per cercare un corso specifico o @grades per aprire la pagina dei voti di un corso, oppure @home per andare alla tua dashboard." "message": "Inizia a digitare per cercare nei tuoi moduli e compiti.\n@coursename (come @chemistry chapter 5 test) cerca un corso specifico o la sua home\n@grades apre la pagina dei voti di un corso\n@home ti porta alla tua dashboard"
}, },
"search_navigate": { "search_navigate": {
"message": "naviga" "message": "naviga"

View File

@ -1416,7 +1416,7 @@
"message": "機能を検索..." "message": "機能を検索..."
}, },
"search_hint": { "search_hint": {
"message": "モジュールや課題を検索するには入力してください。@コース名で特定のコースを検索、@gradesでコースの成績ページを開けます、または @home でダッシュボードに移動できます。" "message": "入力してモジュールと課題を検索します。\n@コース名(例: @chemistry chapter 5 test)で特定のコースまたはそのホームページを検索\n@grades でコースの成績ページを開く\n@home でダッシュボードに移動"
}, },
"search_navigate": { "search_navigate": {
"message": "移動" "message": "移動"

View File

@ -1416,7 +1416,7 @@
"message": "Pesquisar funcionalidades..." "message": "Pesquisar funcionalidades..."
}, },
"search_hint": { "search_hint": {
"message": "Começa a escrever para procurar módulos e trabalhos. Usa @nomedocurso para procurar num curso específico, ou @grades para abrir a página de notas de um curso, ou @home para ires para o teu painel." "message": "Começa a escrever para procurar nos teus módulos e trabalhos.\n@coursename (como @chemistry chapter 5 test) procura uma disciplina específica ou a sua página inicial\n@grades abre a página de notas de uma disciplina\n@home leva-te ao teu painel"
}, },
"search_navigate": { "search_navigate": {
"message": "navegar" "message": "navegar"

View File

@ -1416,7 +1416,7 @@
"message": "Поиск функций..." "message": "Поиск функций..."
}, },
"search_hint": { "search_hint": {
"message": "Начните вводить текст, чтобы искать модули и задания. Используйте @названиекурса для поиска по конкретному курсу или @grades, чтобы открыть страницу оценок курса, или @home, чтобы перейти на главную." "message": "Начните вводить текст, чтобы искать по модулям и заданиям.\n@coursename (например, @chemistry chapter 5 test) ищет конкретный курс или его главную страницу\n@grades открывает страницу оценок курса\n@home переходит на главную"
}, },
"search_navigate": { "search_navigate": {
"message": "перемещение" "message": "перемещение"

View File

@ -1416,7 +1416,7 @@
"message": "Sök funktioner..." "message": "Sök funktioner..."
}, },
"search_hint": { "search_hint": {
"message": "Börja skriva för att söka bland moduler och uppgifter. Använd @kursnamn för att söka i en viss kurs, eller @grades för att öppna en kurs betygssida, eller @home för att gå till din översikt." "message": "Börja skriva för att söka i dina moduler och uppgifter.\n@coursename (till exempel @chemistry chapter 5 test) söker en viss kurs eller dess startsida\n@grades öppnar en kurs resultatsida\n@home tar dig till din översikt"
}, },
"search_navigate": { "search_navigate": {
"message": "navigera" "message": "navigera"

View File

@ -1416,7 +1416,7 @@
"message": "搜索功能..." "message": "搜索功能..."
}, },
"search_hint": { "search_hint": {
"message": "开始输入以搜索你的模块和作业。使用 @课程名 搜索特定课程,或使用 @grades 打开课程的成绩页面,或输入 @home 跳转到你的仪表板。" "message": "开始输入以搜索你的模块和作业。\n@课程名(例如 @chemistry chapter 5 test)可搜索特定课程或其主页\n@grades 打开课程的成绩页面\n@home 跳转到你的仪表板"
}, },
"search_navigate": { "search_navigate": {
"message": "导航" "message": "导航"

View File

@ -1416,7 +1416,7 @@
"message": "搜尋功能..." "message": "搜尋功能..."
}, },
"search_hint": { "search_hint": {
"message": "開始輸入以搜尋你的課程模組和作業。使用 @課程名稱 搜尋特定課程,或使用 @grades 開啟課程的成績頁面,或輸入 @home 前往你的儀表板。" "message": "開始輸入以搜尋你的模組和作業。\n@課程名稱(例如 @chemistry chapter 5 test)可搜尋特定課程或其首頁\n@grades 開啟課程的成績頁面\n@home 前往你的儀表板"
}, },
"search_navigate": { "search_navigate": {
"message": "瀏覽" "message": "瀏覽"

View File

@ -339,6 +339,8 @@
.canvasrefined-gs-results { flex: 1; overflow-y: auto; padding: 6px; } .canvasrefined-gs-results { flex: 1; overflow-y: auto; padding: 6px; }
.canvasrefined-gs-hint, .canvasrefined-gs-loading { .canvasrefined-gs-hint, .canvasrefined-gs-loading {
padding: 18px 14px; text-align: center; color: var(--bctext-2, #9aa3ab); font-size: 14px; padding: 18px 14px; text-align: center; color: var(--bctext-2, #9aa3ab); font-size: 14px;
/* the shortcut hint is one tip per line */
white-space: pre-line; line-height: 1.6;
} }
.canvasrefined-gs-row { .canvasrefined-gs-row {
display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 8px; display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 8px;

View File

@ -10225,7 +10225,7 @@ function openGlobalSearchModal() {
requestAnimationFrame(() => input.focus()); requestAnimationFrame(() => input.focus());
ensureGlobalSearchIndex(); ensureGlobalSearchIndex();
gsUpdateStatus(true); gsUpdateStatus(true);
resultsEl.innerHTML = `<div class="canvasrefined-gs-hint">${getMsg("search_hint", "Start typing to search your modules and assignments. Use @coursename to search a specific course, or @grades to open a course's grades page.")}</div>`; resultsEl.innerHTML = `<div class="canvasrefined-gs-hint">${getMsg("search_hint", "Start typing to search your modules and assignments.\n@coursename (like @chemistry chapter 5 test) to search a specific course or its homepage\n@grades to open a course's grades page\n@home to jump to your dashboard")}</div>`;
} }
function closeGlobalSearchModal() { function closeGlobalSearchModal() {
@ -11000,7 +11000,7 @@ async function runGlobalSearch(query, resultsEl) {
const indexing = gsIsIndexing(); const indexing = gsIsIndexing();
if (!query) { if (!query) {
resultsEl.innerHTML = `<div class="canvasrefined-gs-hint">${getMsg("search_hint", "Start typing to search your modules and assignments. Use @coursename to search a specific course, or @grades to open a course's grades page.")}</div>`; resultsEl.innerHTML = `<div class="canvasrefined-gs-hint">${getMsg("search_hint", "Start typing to search your modules and assignments.\n@coursename (like @chemistry chapter 5 test) to search a specific course or its homepage\n@grades to open a course's grades page\n@home to jump to your dashboard")}</div>`;
return []; return [];
} }
@ -11087,6 +11087,12 @@ async function runGlobalSearch(query, resultsEl) {
if (at === 0) score = 100; if (at === 0) score = 100;
else if (at > 0) score = 60 - at; else if (at > 0) score = 60 - at;
else if (item._c.includes(q)) score = 20; else if (item._c.includes(q)) score = 20;
// Nothing literal? fall back to a fuzzy hit, capped so it never outranks a
// real substring match.
if (score < 0) {
const fuzzy = gsFuzzyScore(item._t, q);
if (fuzzy >= 0) score = Math.max(1, Math.min(19, Math.round(fuzzy / 3)));
}
if (score >= 0) { if (score >= 0) {
if (item.kind === "course" && at === 0) score += 40; if (item.kind === "course" && at === 0) score += 40;
item._score = score + (item._t === q ? 50 : 0); item._score = score + (item._t === q ? 50 : 0);
@ -11123,6 +11129,36 @@ function escapeGlobalSearchAttr(s) {
return escapeGlobalSearchHtml(s).replace(/`/g, "&#96;"); return escapeGlobalSearchHtml(s).replace(/`/g, "&#96;");
} }
// Fuzzy (subsequence) match: every query character appears in the text, in order,
// gaps allowed — "alg2hw" finds "Algebra 2 Homework 5". Word starts and runs of
// adjacent characters score best, skipped characters cost, so tighter matches win.
// Returns -1 when the text does not contain the query as a subsequence.
const GS_FUZZY_MIN_LEN = 3;
function gsFuzzyScore(text, q) {
if (!text || q.length < GS_FUZZY_MIN_LEN || q.length > text.length) return -1;
let from = 0;
let score = 0;
let prev = -2;
let streak = 0;
for (let qi = 0; qi < q.length; qi++) {
const ch = q[qi];
let hit = -1;
for (let i = from; i < text.length; i++) {
if (text[i] === ch) { hit = i; break; }
}
if (hit < 0) return -1;
const before = hit === 0 ? " " : text[hit - 1];
// word starts are the strongest signal a human would pick
score += " -_/.:(".includes(before) ? 6 : 1;
streak = hit === prev + 1 ? streak + 1 : 0;
score += Math.min(streak, 5) * 2;
score -= Math.min(12, hit - from);
prev = hit;
from = hit + 1;
}
return score;
}
function changeGradientCards() { function changeGradientCards() {
// Inline per-hero gradient rather than index-based CSS: it always uses that // Inline per-hero gradient rather than index-based CSS: it always uses that
// hero's current color, so it survives reordering and recoloring. // hero's current color, so it survives reordering and recoloring.