Skip to content

Commit ada579a

Browse files
committed
Fix AI chat submit and table styling
1 parent b165e8d commit ada579a

1 file changed

Lines changed: 182 additions & 11 deletions

File tree

src/components/SearchModal.tsx

Lines changed: 182 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ const AI_DOCK_DEFAULT_WIDTH = 360
225225
const AI_DOCK_MAX_WIDTH_RATIO = 0.5
226226
const AI_DOCK_MAXIMIZED_WIDTH = 1200
227227
const DEFAULT_SEARCH_FRAMEWORK: Framework = 'react'
228+
const KAPA_RECAPTCHA_READY_TIMEOUT_MS = 8_000
228229

229230
type SearchSurface = 'modal' | 'dock'
230231
type AiDockStyle = React.CSSProperties & {
@@ -268,6 +269,71 @@ function writeAiDockWidth(width: number) {
268269
localStorage.setItem(AI_DOCK_WIDTH_STORAGE_KEY, String(Math.round(width)))
269270
}
270271

272+
function waitForKapaRecaptchaReady() {
273+
if (typeof window === 'undefined') {
274+
return Promise.resolve()
275+
}
276+
277+
return new Promise<void>((resolve) => {
278+
let timeoutId: number | null = null
279+
let intervalId: number | null = null
280+
let isSettled = false
281+
let hasRegisteredReadyCallback = false
282+
283+
const cleanup = () => {
284+
if (timeoutId !== null) {
285+
window.clearTimeout(timeoutId)
286+
timeoutId = null
287+
}
288+
289+
if (intervalId !== null) {
290+
window.clearInterval(intervalId)
291+
intervalId = null
292+
}
293+
}
294+
295+
const finish = () => {
296+
if (isSettled) {
297+
return
298+
}
299+
300+
isSettled = true
301+
cleanup()
302+
window.setTimeout(resolve, 0)
303+
}
304+
305+
const checkReady = () => {
306+
if (hasRegisteredReadyCallback) {
307+
return true
308+
}
309+
310+
const enterprise = window.grecaptcha?.enterprise
311+
312+
if (!enterprise) {
313+
return false
314+
}
315+
316+
hasRegisteredReadyCallback = true
317+
318+
if (intervalId !== null) {
319+
window.clearInterval(intervalId)
320+
intervalId = null
321+
}
322+
323+
enterprise.ready(finish)
324+
return true
325+
}
326+
327+
timeoutId = window.setTimeout(finish, KAPA_RECAPTCHA_READY_TIMEOUT_MS)
328+
329+
if (checkReady()) {
330+
return
331+
}
332+
333+
intervalId = window.setInterval(checkReady, 50)
334+
})
335+
}
336+
271337
function buildSearchFilters({
272338
selectedLibrary,
273339
selectedFramework,
@@ -627,10 +693,107 @@ function KapaMarkdownLink({
627693
)
628694
}
629695

696+
function KapaMarkdownTable({
697+
children,
698+
className,
699+
...props
700+
}: React.TableHTMLAttributes<HTMLTableElement>) {
701+
return (
702+
<div className="my-3 max-w-full overflow-x-auto rounded-xl border border-gray-200 bg-white dark:border-white/[0.12] dark:bg-black/20">
703+
<table
704+
className={twMerge(
705+
className,
706+
'w-full min-w-[34rem] border-collapse text-left',
707+
)}
708+
{...props}
709+
>
710+
{children}
711+
</table>
712+
</div>
713+
)
714+
}
715+
716+
function KapaMarkdownTableHead({
717+
children,
718+
className,
719+
...props
720+
}: React.HTMLAttributes<HTMLTableSectionElement>) {
721+
return (
722+
<thead
723+
className={twMerge(
724+
className,
725+
'border-b border-gray-200 bg-gray-50/80 dark:border-white/[0.12] dark:bg-white/[0.04]',
726+
)}
727+
{...props}
728+
>
729+
{children}
730+
</thead>
731+
)
732+
}
733+
734+
function KapaMarkdownTableBody({
735+
children,
736+
className,
737+
...props
738+
}: React.HTMLAttributes<HTMLTableSectionElement>) {
739+
return (
740+
<tbody
741+
className={twMerge(
742+
className,
743+
'[&_tr:last-child_td]:border-b-0 [&_tr:last-child_th]:border-b-0',
744+
)}
745+
{...props}
746+
>
747+
{children}
748+
</tbody>
749+
)
750+
}
751+
752+
function KapaMarkdownTableCell({
753+
children,
754+
className,
755+
...props
756+
}: React.TdHTMLAttributes<HTMLTableCellElement>) {
757+
return (
758+
<td
759+
className={twMerge(
760+
className,
761+
'border-b border-l border-gray-200/80 px-3 py-2 align-top first:border-l-0 dark:border-white/[0.08]',
762+
)}
763+
{...props}
764+
>
765+
{children}
766+
</td>
767+
)
768+
}
769+
770+
function KapaMarkdownTableHeaderCell({
771+
children,
772+
className,
773+
...props
774+
}: React.ThHTMLAttributes<HTMLTableCellElement>) {
775+
return (
776+
<th
777+
className={twMerge(
778+
className,
779+
'border-b border-l border-gray-200 px-3 py-2 font-semibold align-top first:border-l-0 dark:border-white/[0.1]',
780+
)}
781+
{...props}
782+
>
783+
{children}
784+
</th>
785+
)
786+
}
787+
630788
const streamdownComponents = {
631789
pre: CodeBlock,
632790
code: InlineCode,
633791
a: KapaMarkdownLink,
792+
table: KapaMarkdownTable,
793+
thead: KapaMarkdownTableHead,
794+
tbody: KapaMarkdownTableBody,
795+
td: KapaMarkdownTableCell,
796+
th: KapaMarkdownTableHeaderCell,
634797
ul: ({ children }: { children?: React.ReactNode }) => (
635798
<ul className="list-disc list-outside pl-5 space-y-1 my-2">{children}</ul>
636799
),
@@ -699,13 +862,10 @@ class KapaThreadOverrideApiService {
699862
args: KapaSubmitQueryArgs,
700863
callbacks: KapaChatStreamCallbacks,
701864
): Promise<void> {
702-
const threadIdOverride = this.threadIdOverrideRef.current
703-
const nextArgs =
704-
threadIdOverride && !args.threadId
705-
? { ...args, threadId: threadIdOverride }
706-
: args
707-
708-
return this.service.submitQuery(nextArgs, callbacks)
865+
return this.service.submitQuery(
866+
{ ...args, threadId: this.threadIdOverrideRef.current },
867+
callbacks,
868+
)
709869
}
710870

711871
addFeedback(args: KapaSubmitFeedbackArgs): Promise<void> {
@@ -1630,6 +1790,7 @@ function KapaChatPanel({
16301790
const isBusy = isGeneratingAnswer || isPreparingAnswer
16311791
const isDock = surface === 'dock'
16321792
const isSubmittingRef = React.useRef(false)
1793+
const pendingSubmitIdRef = React.useRef(0)
16331794
const lockedToBottom = React.useRef(true)
16341795
const handledNewChatRequestId = React.useRef(newChatRequestId)
16351796
const handledAiDockAskRequestId = React.useRef(0)
@@ -1709,8 +1870,17 @@ function KapaChatPanel({
17091870
return
17101871
}
17111872

1873+
const submitId = pendingSubmitIdRef.current + 1
1874+
pendingSubmitIdRef.current = submitId
17121875
isSubmittingRef.current = true
1713-
submitQuery(trimmed)
1876+
1877+
waitForKapaRecaptchaReady().then(() => {
1878+
if (pendingSubmitIdRef.current !== submitId) {
1879+
return
1880+
}
1881+
1882+
submitQuery(trimmed)
1883+
})
17141884
},
17151885
[isBusy, submitQuery],
17161886
)
@@ -1730,6 +1900,8 @@ function KapaChatPanel({
17301900
)
17311901

17321902
const clearActiveChat = React.useCallback(() => {
1903+
pendingSubmitIdRef.current += 1
1904+
isSubmittingRef.current = false
17331905
resetConversation()
17341906
threadIdOverrideRef.current = null
17351907
setSelectedHistoryItem(null)
@@ -1770,17 +1942,16 @@ function KapaChatPanel({
17701942

17711943
handledAiDockAskRequestId.current = aiDockAskRequest.id
17721944
clearActiveChat()
1773-
isSubmittingRef.current = true
1774-
submitQuery(aiDockAskRequest.question)
1945+
ask(aiDockAskRequest.question)
17751946
clearAiDockAskRequest(aiDockAskRequest.id)
17761947
}, [
1948+
ask,
17771949
aiDockAskRequest,
17781950
clearActiveChat,
17791951
clearAiDockAskRequest,
17801952
isBusy,
17811953
isDock,
17821954
stopGeneration,
1783-
submitQuery,
17841955
])
17851956

17861957
return (

0 commit comments

Comments
 (0)