From 7ff57cd071bb78e7f673d0ae11e32218d6e8aee8 Mon Sep 17 00:00:00 2001 From: Guy Sandler Date: Sun, 25 Oct 2026 16:48:01 -0700 Subject: [PATCH] search improvements --- _locales/de/messages.json | 2 +- _locales/en/messages.json | 2 +- _locales/es/messages.json | 2 +- _locales/fr/messages.json | 2 +- _locales/it/messages.json | 2 +- _locales/ja/messages.json | 2 +- _locales/pt_PT/messages.json | 2 +- _locales/ru/messages.json | 2 +- _locales/sv/messages.json | 2 +- _locales/zh_CN/messages.json | 2 +- _locales/zh_TW/messages.json | 2 +- css/content.css | 2 ++ js/content.js | 40 ++++++++++++++++++++++++++++++++++-- 13 files changed, 51 insertions(+), 13 deletions(-) diff --git a/_locales/de/messages.json b/_locales/de/messages.json index a8b39ff..09f5430 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -1416,7 +1416,7 @@ "message": "Funktionen suchen..." }, "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": { "message": "navigieren" diff --git a/_locales/en/messages.json b/_locales/en/messages.json index c8b78ab..a2e3495 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1440,7 +1440,7 @@ "message": "Search features..." }, "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": { "message": "{{COUNT}} items" diff --git a/_locales/es/messages.json b/_locales/es/messages.json index 65a35ee..22b2972 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -1416,7 +1416,7 @@ "message": "Buscar funciones..." }, "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": { "message": "navegar" diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index 4136267..9f85e3f 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -1416,7 +1416,7 @@ "message": "Rechercher des fonctionnalités..." }, "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": { "message": "naviguer" diff --git a/_locales/it/messages.json b/_locales/it/messages.json index cff7f94..98c8b7a 100644 --- a/_locales/it/messages.json +++ b/_locales/it/messages.json @@ -1416,7 +1416,7 @@ "message": "Cerca funzioni..." }, "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": { "message": "naviga" diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index 208f1bd..cd55b0a 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -1416,7 +1416,7 @@ "message": "機能を検索..." }, "search_hint": { - "message": "モジュールや課題を検索するには入力してください。@コース名で特定のコースを検索、@gradesでコースの成績ページを開けます、または @home でダッシュボードに移動できます。" + "message": "入力してモジュールと課題を検索します。\n@コース名(例: @chemistry chapter 5 test)で特定のコースまたはそのホームページを検索\n@grades でコースの成績ページを開く\n@home でダッシュボードに移動" }, "search_navigate": { "message": "移動" diff --git a/_locales/pt_PT/messages.json b/_locales/pt_PT/messages.json index 24ce2fb..0c8d9c7 100644 --- a/_locales/pt_PT/messages.json +++ b/_locales/pt_PT/messages.json @@ -1416,7 +1416,7 @@ "message": "Pesquisar funcionalidades..." }, "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": { "message": "navegar" diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index bbdc9ec..7a910b0 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -1416,7 +1416,7 @@ "message": "Поиск функций..." }, "search_hint": { - "message": "Начните вводить текст, чтобы искать модули и задания. Используйте @названиекурса для поиска по конкретному курсу или @grades, чтобы открыть страницу оценок курса, или @home, чтобы перейти на главную." + "message": "Начните вводить текст, чтобы искать по модулям и заданиям.\n@coursename (например, @chemistry chapter 5 test) ищет конкретный курс или его главную страницу\n@grades открывает страницу оценок курса\n@home переходит на главную" }, "search_navigate": { "message": "перемещение" diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index 2cba7ef..eeca4df 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1416,7 +1416,7 @@ "message": "Sök funktioner..." }, "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": { "message": "navigera" diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json index 748006d..2677cf9 100644 --- a/_locales/zh_CN/messages.json +++ b/_locales/zh_CN/messages.json @@ -1416,7 +1416,7 @@ "message": "搜索功能..." }, "search_hint": { - "message": "开始输入以搜索你的模块和作业。使用 @课程名 搜索特定课程,或使用 @grades 打开课程的成绩页面,或输入 @home 跳转到你的仪表板。" + "message": "开始输入以搜索你的模块和作业。\n@课程名(例如 @chemistry chapter 5 test)可搜索特定课程或其主页\n@grades 打开课程的成绩页面\n@home 跳转到你的仪表板" }, "search_navigate": { "message": "导航" diff --git a/_locales/zh_TW/messages.json b/_locales/zh_TW/messages.json index f5f3201..debc44f 100644 --- a/_locales/zh_TW/messages.json +++ b/_locales/zh_TW/messages.json @@ -1416,7 +1416,7 @@ "message": "搜尋功能..." }, "search_hint": { - "message": "開始輸入以搜尋你的課程模組和作業。使用 @課程名稱 搜尋特定課程,或使用 @grades 開啟課程的成績頁面,或輸入 @home 前往你的儀表板。" + "message": "開始輸入以搜尋你的模組和作業。\n@課程名稱(例如 @chemistry chapter 5 test)可搜尋特定課程或其首頁\n@grades 開啟課程的成績頁面\n@home 前往你的儀表板" }, "search_navigate": { "message": "瀏覽" diff --git a/css/content.css b/css/content.css index 2ea8d62..48fd9dd 100644 --- a/css/content.css +++ b/css/content.css @@ -339,6 +339,8 @@ .canvasrefined-gs-results { flex: 1; overflow-y: auto; padding: 6px; } .canvasrefined-gs-hint, .canvasrefined-gs-loading { 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 { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 8px; diff --git a/js/content.js b/js/content.js index b299b77..a10aa8d 100644 --- a/js/content.js +++ b/js/content.js @@ -10225,7 +10225,7 @@ function openGlobalSearchModal() { requestAnimationFrame(() => input.focus()); ensureGlobalSearchIndex(); gsUpdateStatus(true); - resultsEl.innerHTML = `
${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.")}
`; + resultsEl.innerHTML = `
${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")}
`; } function closeGlobalSearchModal() { @@ -11000,7 +11000,7 @@ async function runGlobalSearch(query, resultsEl) { const indexing = gsIsIndexing(); if (!query) { - resultsEl.innerHTML = `
${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.")}
`; + resultsEl.innerHTML = `
${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")}
`; return []; } @@ -11087,6 +11087,12 @@ async function runGlobalSearch(query, resultsEl) { if (at === 0) score = 100; else if (at > 0) score = 60 - at; 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 (item.kind === "course" && at === 0) score += 40; item._score = score + (item._t === q ? 50 : 0); @@ -11123,6 +11129,36 @@ function escapeGlobalSearchAttr(s) { return escapeGlobalSearchHtml(s).replace(/`/g, "`"); } +// 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() { // Inline per-hero gradient rather than index-based CSS: it always uses that // hero's current color, so it survives reordering and recoloring.