Skip to content

Commit 03b4a5b

Browse files
committed
Add Kapa AI to the search widget
Signed-off-by: Julius Volz <julius.volz@gmail.com>
1 parent 80809ac commit 03b4a5b

7 files changed

Lines changed: 97 additions & 14 deletions

File tree

docs-config.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@ export default {
55

66
announcement: {
77
text: "Take the [Prometheus User Survey (Edition 03.2026)](https://forms.gle/uuEsawKm7u9wCT4T8) and help the community prioritize future development!",
8-
mobileText: "[Prometheus User Survey (Edition 03.2026)](https://forms.gle/uuEsawKm7u9wCT4T8)",
8+
mobileText:
9+
"[Prometheus User Survey (Edition 03.2026)](https://forms.gle/uuEsawKm7u9wCT4T8)",
910
startDate: "2026-03-24",
1011
endDate: "2026-04-07",
1112
},
1213

14+
kapa: {
15+
websiteId: "80cbacc9-0b84-48aa-bfb8-0002270176bf",
16+
projectName: "Prometheus",
17+
projectColor: "#D86444",
18+
projectLogoPath: "/assets/prometheus-logo.svg",
19+
},
20+
1321
// Docs to load from repo-local files.
1422
localMarkdownSources: [
1523
{

public/assets/prometheus-logo.svg

Lines changed: 1 addition & 0 deletions
Loading

src/app/layout.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import "@mantine/code-highlight/styles.layer.css";
2121
import "@mantine/spotlight/styles.layer.css";
2222
import "./globals.css";
2323
import { Header } from "@/components/Header";
24+
import KapaWidget from "@/components/KapaWidget";
2425
import {
2526
ANNOUNCEMENT_HEIGHT_PX,
2627
isAnnouncementActive,
@@ -53,8 +54,7 @@ export default function RootLayout({
5354
children: React.ReactNode;
5455
}>) {
5556
const activeAnnouncement =
56-
docsConfig.announcement &&
57-
isAnnouncementActive(docsConfig.announcement)
57+
docsConfig.announcement && isAnnouncementActive(docsConfig.announcement)
5858
? docsConfig.announcement
5959
: undefined;
6060

@@ -67,13 +67,16 @@ export default function RootLayout({
6767
lang="en"
6868
{...mantineHtmlProps}
6969
className={`${interFont.variable} ${latoFont.variable}`}
70-
style={{ "--header-height": `${headerHeightPx}px` } as React.CSSProperties}
70+
style={
71+
{ "--header-height": `${headerHeightPx}px` } as React.CSSProperties
72+
}
7173
>
7274
<head>
7375
<ColorSchemeScript defaultColorScheme="auto" />
7476
</head>
7577
<body>
7678
<MantineProvider theme={theme} defaultColorScheme="auto">
79+
<KapaWidget />
7780
<AppShell header={{ height: "var(--header-height)" }}>
7881
<Header announcement={activeAnnouncement} />
7982

src/components/Header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export const Header = ({
141141

142142
<Group visibleFrom="md" gap="xs">
143143
<TextInput
144-
placeholder="Search"
144+
placeholder="Search / Ask AI"
145145
w={220}
146146
mx="lg"
147147
leftSection={

src/components/KapaWidget.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Script from "next/script";
2+
import docsConfig from "../../docs-config";
3+
4+
export default function KapaWidget() {
5+
if (!docsConfig.kapa) {
6+
return null;
7+
}
8+
9+
const { websiteId, projectName, projectColor, projectLogoPath } =
10+
docsConfig.kapa;
11+
const projectLogoUrl = new URL(
12+
projectLogoPath,
13+
docsConfig.siteUrl,
14+
).toString();
15+
16+
return (
17+
<Script
18+
id="kapa-widget"
19+
strategy="afterInteractive"
20+
src="https://widget.kapa.ai/kapa-widget.bundle.js"
21+
data-website-id={websiteId}
22+
data-project-name={projectName}
23+
data-project-color={projectColor}
24+
data-project-logo={projectLogoUrl}
25+
data-button-hide="true"
26+
/>
27+
);
28+
}

src/components/SpotlightSearch.tsx

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
"use client";
22

3-
import { Spotlight } from "@mantine/spotlight";
4-
import { IconSearch } from "@tabler/icons-react";
3+
import { Spotlight, spotlight } from "@mantine/spotlight";
4+
import { IconSearch, IconSparkles } from "@tabler/icons-react";
55
import { useRouter } from "next/navigation";
6-
import { Divider, Group, Highlight, Loader, Space } from "@mantine/core";
7-
import React, { useState, useEffect } from "react";
6+
import {
7+
Button,
8+
Divider,
9+
Group,
10+
Highlight,
11+
Loader,
12+
Space,
13+
} from "@mantine/core";
14+
import React, { useState, useEffect, useRef } from "react";
815
import { decode } from "html-entities";
916

10-
// Extend Window interface to include pagefind
17+
// Extend Window interface to include pagefind and Kapa
1118
declare global {
1219
interface Window {
1320
/* eslint-disable @typescript-eslint/no-explicit-any */
1421
pagefind: any;
22+
Kapa: any;
1523
}
1624
}
1725

@@ -127,6 +135,7 @@ type PagefindResult = {
127135
export default function SpotlightSearch() {
128136
const [activeQuery, setActiveQuery] = useState("");
129137
const [results, setResults] = useState<PagefindResult[]>([]);
138+
const queryRef = useRef("");
130139

131140
useEffect(() => {
132141
async function loadPagefind() {
@@ -175,12 +184,24 @@ export default function SpotlightSearch() {
175184
loadPagefind();
176185
}, []);
177186

187+
const handleAskAI = () => {
188+
const query = queryRef.current.trim();
189+
spotlight.close();
190+
// Small delay to let the Spotlight close animation finish before opening Kapa.
191+
setTimeout(() => {
192+
if (window.Kapa) {
193+
window.Kapa.open({ mode: "ai", query, submit: query.length > 0 });
194+
}
195+
}, 100);
196+
};
197+
178198
return (
179199
<Spotlight.Root
180200
size="xl"
181201
maxHeight="90vh"
182202
scrollable
183203
onQueryChange={async (query) => {
204+
queryRef.current = query;
184205
const search = await window.pagefind.debouncedSearch(query);
185206
if (search === null) {
186207
// A more recent search call has been made, nothing to do.
@@ -190,10 +211,24 @@ export default function SpotlightSearch() {
190211
setActiveQuery(query);
191212
}}
192213
>
193-
<Spotlight.Search
194-
placeholder="Search..."
195-
leftSection={<IconSearch stroke={1.5} />}
196-
/>
214+
<Group gap={0} align="center" wrap="nowrap">
215+
<Spotlight.Search
216+
placeholder="Search..."
217+
leftSection={<IconSearch stroke={1.5} />}
218+
style={{ flex: 1 }}
219+
/>
220+
<Button
221+
variant="light"
222+
size="compact-md"
223+
onClick={handleAskAI}
224+
leftSection={<IconSparkles size={16} stroke={1.8} />}
225+
mr="xs"
226+
fw={500}
227+
style={{ flexShrink: 0 }}
228+
>
229+
Ask AI
230+
</Button>
231+
</Group>
197232
<Spotlight.ActionsList>
198233
{results.length > 0 ? (
199234
results.map((result, idx) => (

src/docs-config-types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ export type Announcement = {
1010
endDate?: string;
1111
};
1212

13+
export type KapaConfig = {
14+
websiteId: string;
15+
projectName: string;
16+
projectColor: string;
17+
projectLogoPath: string;
18+
};
19+
1320
export type DocsConfig = {
1421
siteUrl: string;
1522
announcement?: Announcement;
23+
kapa?: KapaConfig;
1624
localMarkdownSources: LocalMarkdownSource[];
1725
githubMarkdownSources: GithubMarkdownSource[];
1826
ltsVersions: LTSConfig;

0 commit comments

Comments
 (0)