Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/lib/algolia-ask-ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,21 @@ function highlightedAskAiQuery(): string | null {
* message DocSearch renders is the one it sends.
*
* Replaying is abandoned if the user keeps typing or leaves, and falls through to the original
* behaviour on timeout so Enter can never be swallowed outright.
* behaviour on timeout so Enter can never be swallowed outright. At most one wait is ever pending:
* a later Enter supersedes it and uninstall clears it, so a replay can never land after the
* question it was holding back has already been asked.
*/
function installStaleQuestionGuard(): () => void {
let replaying = false
let poll: number | undefined

const stopPoll = () => {
if (poll === undefined) {
return
}
window.clearInterval(poll)
poll = undefined
}

const submit = (input: HTMLInputElement) => {
replaying = true
Expand All @@ -101,6 +112,10 @@ function installStaleQuestionGuard(): () => void {
return
}

// Every Enter the user presses supersedes a wait already in flight, including one this guard
// lets through: DocSearch acts on it there and then, so replaying afterwards would ask twice.
stopPoll()

const optionQuery = highlightedAskAiQuery()
const intendedQuery = input.value
if (optionQuery === null || normalizeQuery(optionQuery) === normalizeQuery(intendedQuery)) {
Expand All @@ -111,18 +126,18 @@ function installStaleQuestionGuard(): () => void {
event.stopImmediatePropagation()

const deadline = Date.now() + OPTION_CATCH_UP_TIMEOUT_MS
const poll = window.setInterval(() => {
poll = window.setInterval(() => {
const caughtUp =
normalizeQuery(highlightedAskAiQuery() ?? '') === normalizeQuery(intendedQuery)
const abandoned = !input.isConnected || input.value !== intendedQuery

if (abandoned) {
window.clearInterval(poll)
stopPoll()
return
}

if (caughtUp || Date.now() > deadline) {
window.clearInterval(poll)
stopPoll()
submit(input)
}
}, OPTION_CATCH_UP_POLL_MS)
Expand All @@ -131,6 +146,7 @@ function installStaleQuestionGuard(): () => void {
document.addEventListener('keydown', onKeyDown, true)

return () => {
stopPoll()
document.removeEventListener('keydown', onKeyDown, true)
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/scss/theme/_doc-search.scss
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@
}
}

// Ask AI renders a fenced code block as `<pre><code class="language-js">`, which the site's Prism
// theme claims with `!important` and paints in the light gray it uses on the dark code blocks of
// doc pages. DocSearch backs its code blocks with the modal surface instead, so in light mode that
// gray lands on near-white. Matched from the document because this markup is shared by the search
// modal and the sidepanel.
.DocSearch-Markdown-Content [class*='language-'] {
color: var(--docsearch-text-color) !important;
}

.main-wrapper {
.search-page-wrapper & {
margin: auto;
Expand Down
Loading