Skip to content

Commit c745153

Browse files
johnlarkin1claude
andauthored
fix(search): hybrid search fallback and scroll containment (#101)
- Allow keyword-only searching while semantic model loads in hybrid mode - Add status badge showing "Loading semantic model..." during download - Fix scroll containment in semantic/hybrid results with overscroll-behavior - Call initHybridSearch/initVectorSearch on first modal open 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 3c7fb7a commit c745153

File tree

5 files changed

+60
-24
lines changed

5 files changed

+60
-24
lines changed

_includes/search-modal.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ <h2 id="search-modal-title" class="search-modal__title">Search</h2>
129129
>
130130
</div>
131131

132+
<!-- Status badge for model loading/fallback -->
133+
<div class="hybrid-search__status" style="display: none;"></div>
134+
132135
<!-- Results -->
133136
<div class="hybrid-search__results"></div>
134137

_js/scripts.js

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,36 +1664,41 @@ function renderVectorResults(results) {
16641664
}
16651665

16661666
function initHybridSearch() {
1667+
var $status = $(".hybrid-search__status");
1668+
var $inputWrapper = $(".hybrid-search__input").closest(".vector-search__input-wrapper");
1669+
var $loading = $(".hybrid-search__loading");
1670+
16671671
if (window.VectorSearch.isAvailable()) {
1668-
// Already initialized, focus input
1669-
$(".hybrid-search__loading").hide();
1670-
$(".hybrid-search__input").closest(".vector-search__input-wrapper").show();
1672+
// Model ready - hide loading/status, show input
1673+
$loading.hide();
1674+
$inputWrapper.show();
1675+
$status.hide();
16711676
$(".hybrid-search__input").focus();
16721677
return;
16731678
}
16741679

1675-
if (window.VectorSearch.isLoading()) {
1676-
return; // Already loading
1677-
}
1678-
1679-
// Show loading state
1680-
$(".hybrid-search__loading").show();
1681-
$(".hybrid-search__input").closest(".vector-search__input-wrapper").hide();
1680+
// Always show input immediately (allow keyword-only search while model loads)
1681+
$loading.hide();
1682+
$inputWrapper.show();
16821683
$(".hybrid-search__results").empty();
16831684
$(".hybrid-search__empty").hide();
1685+
$(".hybrid-search__input").focus();
1686+
1687+
// Show status badge indicating model is loading
1688+
$status.html('<span class="hybrid-search__status-spinner"></span> Loading semantic model...').show();
1689+
1690+
if (window.VectorSearch.isLoading()) {
1691+
return; // Already loading, status badge is showing
1692+
}
16841693

16851694
// Initialize with progress callback
16861695
window.VectorSearch.initialize(function (info) {
1687-
$(".hybrid-search__loading .vector-search__loading-message").text(info.message);
1688-
$(".hybrid-search__loading .vector-search__loading-progress").css("width", info.progress + "%");
1689-
16901696
if (info.stage === "ready") {
1691-
$(".hybrid-search__loading").hide();
1692-
$(".hybrid-search__input").closest(".vector-search__input-wrapper").show();
1693-
$(".hybrid-search__input").focus();
1697+
// Model ready - hide status badge
1698+
$status.hide();
16941699
} else if (info.stage === "error") {
1695-
$(".hybrid-search__loading .vector-search__loading-message").text("Failed to load: " + info.message);
1696-
$(".hybrid-search__loading .vector-search__loading-spinner").hide();
1700+
// Model failed - show keyword-only status
1701+
$status.html('Keyword only (semantic unavailable)').show();
16971702
}
16981703
});
16991704
}
@@ -1950,15 +1955,15 @@ function openSearch() {
19501955
window.pagefindInitialized = true;
19511956
}
19521957

1953-
// Focus appropriate input based on mode
1958+
// Initialize and focus appropriate input based on mode
19541959
setTimeout(function () {
19551960
if (searchMode === "keyword") {
19561961
var input = $modal.find(".pagefind-ui__search-input")[0];
19571962
if (input) input.focus();
19581963
} else if (searchMode === "semantic") {
1959-
$(".vector-search__input").focus();
1964+
initVectorSearch();
19601965
} else if (searchMode === "hybrid") {
1961-
$(".hybrid-search__input").focus();
1966+
initHybridSearch();
19621967
}
19631968
}, 150);
19641969

_sass/components/_search.scss

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ mark.pagefind-ui__result-highlight {
771771
max-height: 420px;
772772
overflow-y: auto;
773773
overflow-x: hidden;
774+
overscroll-behavior: contain;
774775

775776
// Refined scrollbar
776777
&::-webkit-scrollbar {
@@ -1035,6 +1036,7 @@ mark.pagefind-ui__result-highlight {
10351036
max-height: 420px;
10361037
overflow-y: auto;
10371038
overflow-x: hidden;
1039+
overscroll-behavior: contain;
10381040

10391041
// Match scrollbar styling
10401042
&::-webkit-scrollbar {
@@ -1101,4 +1103,30 @@ mark.pagefind-ui__result-highlight {
11011103
padding: 3rem 1.5rem;
11021104
gap: 1rem;
11031105
}
1106+
1107+
// Status badge for model loading/fallback
1108+
&__status {
1109+
display: flex;
1110+
align-items: center;
1111+
gap: 0.5rem;
1112+
padding: 0.5rem 0.75rem;
1113+
margin: 0 0.5rem 0.5rem;
1114+
font-family: "SF Mono", "JetBrains Mono", monospace;
1115+
font-size: 0.6875rem;
1116+
font-weight: 500;
1117+
letter-spacing: 0.02em;
1118+
color: rgba(255, 180, 50, 0.9);
1119+
background: rgba(255, 180, 50, 0.1);
1120+
border-radius: 6px;
1121+
}
1122+
1123+
&__status-spinner {
1124+
display: inline-block;
1125+
width: 10px;
1126+
height: 10px;
1127+
border: 1.5px solid rgba(255, 180, 50, 0.3);
1128+
border-top-color: rgba(255, 180, 50, 0.9);
1129+
border-radius: 50%;
1130+
animation: spin 0.8s linear infinite;
1131+
}
11041132
}

assets/css/main.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/bundle.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)