This is an automated email from the ASF dual-hosted git repository.
kparisa pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iggy-website.git
The following commit(s) were added to refs/heads/main by this push:
new 8ed2c7856 feat(site): add the kapa.ai Ask the docs widget (#123)
8ed2c7856 is described below
commit 8ed2c785604ab979424dc4c7ce3089f015a24abe
Author: Justin Mclean <[email protected]>
AuthorDate: Tue Oct 6 17:59:14 2026 +1100
feat(site): add the kapa.ai Ask the docs widget (#123)
Adds the widget behind a consent screen, with analytics off, and an Ask
the docs navbar button that opens it. Allows the kapa and reCAPTCHA
domains through the ASF CSP, as approved in the ASF privacy policy.
---------
Co-authored-by: Justin Mclean <[email protected]>
Co-authored-by: Kranti Parisa <[email protected]>
---
.htaccess | 6 ++++++
src/app/layout.tsx | 29 +++++++++++++++++++++++++++++
src/components/ask-ai-button.tsx | 34 ++++++++++++++++++++++++++++++++++
src/lib/layout.shared.tsx | 11 +++++++++++
4 files changed, 80 insertions(+)
diff --git a/.htaccess b/.htaccess
index c2733fadd..3c4ba479e 100644
--- a/.htaccess
+++ b/.htaccess
@@ -21,3 +21,9 @@
# "Not Found" body and the 404.html produced by `next build` is never used.
# The status code stays 404 either way; this only replaces the body.
ErrorDocument 404 /404.html
+
+# CSP permissions for iggy.apache.org: the kapa.ai "Ask the docs" widget and
+# the hCaptcha bot check it is configured to use. kapa.ai is approved by VP
+# Data Privacy in the ASF privacy policy:
+# https://privacy.apache.org/policies/privacy-policy-public.html#i-kapaai
+SetEnv CSP_PROJECT_DOMAINS "https://widget.kapa.ai/ https://proxy.kapa.ai/
https://kapa-widget-proxy-la7dkmplpq-uc.a.run.app/ https://metrics.kapa.ai/
https://hcaptcha.com/ https://*.hcaptcha.com/"
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index b11f6bb8f..213979f2f 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -137,6 +137,35 @@ _paq.push(['enableLinkTracking']);
})();`}
</Script>
<Matomo />
+ {/*
+ kapa.ai "Ask AI" widget, through the Kapa Open Source Program.
kapa.ai
+ is approved in the ASF privacy policy:
+
https://privacy.apache.org/policies/privacy-policy-public.html#i-kapaai
+ The widget script loads on every page, but no question goes to kapa
+ until the visitor accepts the consent screen, and kapa's analytics
+ cookie and fingerprinting are off. The launcher is hidden; the
+ "Ask the docs" button in the navbar opens the modal.
+ */}
+ <Script
+ id="kapa-widget"
+ src="https://widget.kapa.ai/kapa-widget.bundle.js"
+ strategy="afterInteractive"
+ data-website-id="3ddb8a09-e3b6-497a-ba43-b48a5d983815"
+ data-project-name="Apache Iggy"
+ data-modal-title="Ask the Apache Iggy docs"
+ data-modal-ask-ai-input-placeholder="How do I create a consumer
group?"
+ data-project-color="#fb4800"
+ data-project-logo="https://iggy.apache.org/img/favicon.png"
+ data-color-scheme-selector=".dark"
+ data-button-hide="true"
+ data-modal-override-open-class="kapa-ask-ai"
+ data-consent-required="true"
+ data-consent-screen-disclaimer="Ask the docs is run by kapa.ai, a
third-party service. Your questions are sent to kapa.ai to generate answers,
and the form is protected by hCaptcha. By continuing you agree to the [kapa.ai
privacy policy](https://www.kapa.ai/content/privacy-policy) and the [hCaptcha
privacy policy](https://www.hcaptcha.com/privacy)."
+ data-bot-protection-mechanism="hcaptcha"
+ data-user-analytics-cookie-enabled="false"
+ data-user-analytics-fingerprint-enabled="false"
+ data-modal-disclaimer="Answers are generated by AI from the Apache
Iggy docs and code, and can be wrong. Check them against the docs before
relying on them."
+ />
</body>
</html>
);
diff --git a/src/components/ask-ai-button.tsx b/src/components/ask-ai-button.tsx
new file mode 100644
index 000000000..02a91f27c
--- /dev/null
+++ b/src/components/ask-ai-button.tsx
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Opens the kapa.ai "Ask the docs" modal. The widget script in the root
layout is
+ * configured with data-modal-override-open-class="kapa-ask-ai" and hides its
+ * own launcher, so this button is the only way in.
+ */
+export function AskAIButton() {
+ return (
+ <button
+ type="button"
+ className="kapa-ask-ai inline-flex items-center rounded-lg border
border-fd-border bg-fd-secondary/60 px-3 py-1.5 text-sm font-medium
text-fd-foreground transition-colors hover:bg-fd-accent"
+ >
+ Ask the docs
+ </button>
+ );
+}
diff --git a/src/lib/layout.shared.tsx b/src/lib/layout.shared.tsx
index da78ff32e..4bb04a130 100644
--- a/src/lib/layout.shared.tsx
+++ b/src/lib/layout.shared.tsx
@@ -21,6 +21,7 @@ import type { BaseLayoutProps } from
"fumadocs-ui/layouts/shared";
import Image from "next/image";
import { Logo } from "@/components/logo";
import { GitHubStars } from "@/components/github-stars";
+import { AskAIButton } from "@/components/ask-ai-button";
import { ASFDropdown } from "@/components/asf-dropdown";
import { ASFMobileLinks } from "@/components/asf-mobile-links";
import {
@@ -68,6 +69,11 @@ const sharedLinks: BaseLayoutProps["links"] = [
on: "menu",
children: <ASFMobileLinks />,
},
+ {
+ type: "custom",
+ secondary: true,
+ children: <AskAIButton />,
+ },
{
type: "custom",
secondary: true,
@@ -122,6 +128,11 @@ export function docsOptions(): BaseLayoutProps {
children: <CommunityDropdown />,
},
communityMobileMenu,
+ {
+ type: "custom",
+ secondary: true,
+ children: <AskAIButton />,
+ },
{
type: "custom",
on: "nav",