From dcd4e0a66f830e9a4184877c5d8dad9cc7bf3228 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 20 May 2025 17:27:37 +0100 Subject: [PATCH 01/50] Install the kapa sdk --- apps/webapp/package.json | 17 +++++---- pnpm-lock.yaml | 79 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 8 deletions(-) diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 95c843c0eb..b613ba8328 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -45,18 +45,20 @@ "@codemirror/view": "^6.5.0", "@conform-to/react": "0.9.2", "@conform-to/zod": "0.9.2", - "@depot/sdk-node": "^1.0.0", "@depot/cli": "0.0.1-cli.2.80.0", + "@depot/sdk-node": "^1.0.0", "@electric-sql/react": "^0.3.5", "@headlessui/react": "^1.7.8", "@heroicons/react": "^2.0.12", + "@internal/redis": "workspace:*", "@internal/run-engine": "workspace:*", + "@internal/tracing": "workspace:*", "@internal/zod-worker": "workspace:*", - "@internal/redis": "workspace:*", - "@trigger.dev/redis-worker": "workspace:*", "@internationalized/date": "^3.5.1", + "@kapaai/react-sdk": "^0.1.0", "@lezer/highlight": "^1.1.6", "@opentelemetry/api": "1.9.0", + "@opentelemetry/api-logs": "0.52.1", "@opentelemetry/core": "1.25.1", "@opentelemetry/exporter-logs-otlp-http": "0.52.1", "@opentelemetry/exporter-trace-otlp-http": "0.52.1", @@ -69,7 +71,6 @@ "@opentelemetry/sdk-trace-base": "1.25.1", "@opentelemetry/sdk-trace-node": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1", - "@opentelemetry/api-logs": "0.52.1", "@popperjs/core": "^2.11.8", "@prisma/instrumentation": "^5.11.0", "@radix-ui/react-alert-dialog": "^1.0.4", @@ -104,8 +105,8 @@ "@trigger.dev/database": "workspace:*", "@trigger.dev/otlp-importer": "workspace:*", "@trigger.dev/platform": "1.0.14", + "@trigger.dev/redis-worker": "workspace:*", "@trigger.dev/sdk": "workspace:*", - "@internal/tracing": "workspace:*", "@types/pg": "8.6.6", "@uiw/react-codemirror": "^4.19.5", "@unkey/cache": "^1.5.0", @@ -146,8 +147,8 @@ "non.geist": "^1.0.2", "ohash": "^1.1.3", "openai": "^4.33.1", - "parse-duration": "^2.1.0", "p-limit": "^6.2.0", + "parse-duration": "^2.1.0", "posthog-js": "^1.93.3", "posthog-node": "4.17.1", "prism-react-renderer": "^2.3.1", @@ -194,9 +195,9 @@ "zod-validation-error": "^1.5.0" }, "devDependencies": { - "@internal/testcontainers": "workspace:*", - "@internal/replication": "workspace:*", "@internal/clickhouse": "workspace:*", + "@internal/replication": "workspace:*", + "@internal/testcontainers": "workspace:*", "@remix-run/dev": "2.1.0", "@remix-run/eslint-config": "2.1.0", "@remix-run/testing": "^2.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04ccc99c1a..347e312f2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -258,6 +258,9 @@ importers: '@internationalized/date': specifier: ^3.5.1 version: 3.5.1 + '@kapaai/react-sdk': + specifier: ^0.1.0 + version: 0.1.0(react-dom@18.2.0)(react@18.2.0) '@lezer/highlight': specifier: ^1.1.6 version: 1.1.6 @@ -6987,6 +6990,27 @@ packages: resolution: {integrity: sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==} engines: {node: '>=14'} + /@fingerprintjs/fingerprintjs-pro-react@2.6.3: + resolution: {integrity: sha512-/axCq/cfjZkIM+WFZM/05FQvqtNfdKbIFKU6b2yrwPKlgT8BqWkAq8XvFX6JCPlq8/udVLJjFEDCK+1JQh1L6g==} + requiresBuild: true + dependencies: + '@fingerprintjs/fingerprintjs-pro-spa': 1.3.2 + fast-deep-equal: 3.1.3 + dev: false + + /@fingerprintjs/fingerprintjs-pro-spa@1.3.2: + resolution: {integrity: sha512-s1YGsx1XQLmjU+av4UrUHNxyzwPHyZRB0GXJQFOJK8ZHCYc2SNukxnJmZA++bNBa8twU3wW+QgSJhA4Prjnd0g==} + dependencies: + '@fingerprintjs/fingerprintjs-pro': 3.11.9 + tslib: 2.8.1 + dev: false + + /@fingerprintjs/fingerprintjs-pro@3.11.9: + resolution: {integrity: sha512-GTow49I86tBlV/U+edBTLA2dPCDXrfHVsNuTV7a5nZjWFV3Q44Tj6yb5O0GcGT/EHwbSy3UmCO9+PS1ERR+b2g==} + dependencies: + tslib: 2.8.1 + dev: false + /@floating-ui/core@0.7.3: resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==} dev: false @@ -7168,6 +7192,22 @@ packages: '@hapi/hoek': 11.0.2 dev: false + /@hcaptcha/loader@2.0.0: + resolution: {integrity: sha512-fFQH6ApU/zCCl6Y1bnbsxsp1Er/lKX+qlgljrpWDeFcenpEtoP68hExlKSXECospzKLeSWcr06cbTjlR/x3IJA==} + dev: false + + /@hcaptcha/react-hcaptcha@1.12.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-QiHnQQ52k8SJJSHkc3cq4TlYzag7oPd4f5ZqnjVSe4fJDSlZaOQFtu5F5AYisVslwaitdDELPVLRsRJxiiI0Aw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + dependencies: + '@babel/runtime': 7.27.0 + '@hcaptcha/loader': 2.0.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@headlessui/react@1.7.8(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-zcwb0kd7L05hxmoAMIioEaOn235Dg0fUO+iGbLPgLVSjzl/l39V6DTpC2Df49PE5aG5/f5q0PZ9ZHZ78ENNV+A==} engines: {node: '>=10'} @@ -7749,6 +7789,21 @@ packages: resolution: {integrity: sha512-Lg3PnLp0QXpxwLIAuuJboLeRaIhrgJjeuh797QADg3xz8wGLugQOS5DpsE8A6i6Adgzf+bacllkKZG3J0tGfDw==} dev: true + /@kapaai/react-sdk@0.1.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-eyEBG5wk1CnlziYFhPjWcn/Lg5+uVGZ8UbSDs7WXqh0Vwc4Ri+iGqVHgdZ3iHt4bLjzHQB5odftlWoRPzek0YA==} + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + dependencies: + '@fingerprintjs/fingerprintjs-pro-react': 2.6.3 + '@hcaptcha/react-hcaptcha': 1.12.0(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-query': 5.76.1(react@18.2.0) + js-cookie: 3.0.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tldts: 7.0.7 + dev: false + /@kubernetes/client-node@0.20.0: resolution: {integrity: sha512-xxlv5GLX4FVR/dDKEsmi4SPeuB49aRc35stndyxcC73XnUEEwF39vXbROpHOirmDse8WE9vxOjABnSVS+jb7EA==} dependencies: @@ -16838,6 +16893,19 @@ packages: tailwindcss: 4.0.17 dev: true + /@tanstack/query-core@5.76.0: + resolution: {integrity: sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==} + dev: false + + /@tanstack/react-query@5.76.1(react@18.2.0): + resolution: {integrity: sha512-YxdLZVGN4QkT5YT1HKZQWiIlcgauIXEIsMOTSjvyD5wLYK8YVvKZUPAysMqossFJJfDpJW3pFn7WNZuPOqq+fw==} + peerDependencies: + react: ^18 || ^19 + dependencies: + '@tanstack/query-core': 5.76.0 + react: 18.2.0 + dev: false + /@tanstack/react-virtual@3.0.4(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-tiqKW/e2MJVCr7/pRUXulpkyxllaOclkHNfhKTo4pmHjJIqnhMfwIjc1Q1R0Un3PI3kQywywu/791c8z9u0qeA==} peerDependencies: @@ -32598,6 +32666,17 @@ packages: engines: {node: '>=14.0.0'} dev: true + /tldts-core@7.0.7: + resolution: {integrity: sha512-ECqb8imSroX1UmUuhRBNPkkmtZ8mHEenieim80UVxG0M5wXVjY2Fp2tYXCPvk+nLy1geOhFpeD5YQhM/gF63Jg==} + dev: false + + /tldts@7.0.7: + resolution: {integrity: sha512-ETNXj36ql5BXDa4VVJk3wgqansg8TI1Yqo217twSAPjyDnh/b2T+XzrI0ftn6EnzVPbXpMTZHOWj5s3a8/uGPA==} + hasBin: true + dependencies: + tldts-core: 7.0.7 + dev: false + /tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} dependencies: From 6b826802ee0ecfb94770418a5fff7c6045746827 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 20 May 2025 17:28:13 +0100 Subject: [PATCH 02/50] WIP using the SDK for the Kapa Ask AI widget --- apps/webapp/app/components/KapaChat.tsx | 144 +++++++++++++++++ .../app/components/navigation/SideMenu.tsx | 32 ++-- apps/webapp/app/hooks/useKapaWidget.tsx | 145 ------------------ apps/webapp/app/root.tsx | 4 +- 4 files changed, 161 insertions(+), 164 deletions(-) create mode 100644 apps/webapp/app/components/KapaChat.tsx delete mode 100644 apps/webapp/app/hooks/useKapaWidget.tsx diff --git a/apps/webapp/app/components/KapaChat.tsx b/apps/webapp/app/components/KapaChat.tsx new file mode 100644 index 0000000000..d0c5782391 --- /dev/null +++ b/apps/webapp/app/components/KapaChat.tsx @@ -0,0 +1,144 @@ +import { XMarkIcon } from "@heroicons/react/20/solid"; +import { ArrowUpIcon } from "@heroicons/react/24/solid"; +import { KapaProvider, useChat } from "@kapaai/react-sdk"; +import { useSearchParams } from "@remix-run/react"; +import { useCallback, useEffect, useState } from "react"; +import { AISparkleIcon } from "~/assets/icons/AISparkleIcon"; +import { Button } from "./primitives/Buttons"; +import { Header2 } from "./primitives/Headers"; +import { Paragraph } from "./primitives/Paragraph"; +import { Spinner } from "./primitives/Spinner"; + +type KapaChatProps = { + websiteId: string; + onOpen?: () => void; + onClose?: () => void; +}; + +function ChatInterface({ onOpen, onClose }: { onOpen?: () => void; onClose?: () => void }) { + const [message, setMessage] = useState(""); + const { conversation, submitQuery, isGeneratingAnswer, isPreparingAnswer } = useChat(); + const [searchParams, setSearchParams] = useSearchParams(); + + // Handle URL param functionality + useEffect(() => { + const aiHelp = searchParams.get("aiHelp"); + if (aiHelp) { + setSearchParams((prev) => { + prev.delete("aiHelp"); + return prev; + }); + + const decodedAiHelp = decodeURIComponent(aiHelp); + submitQuery(decodedAiHelp); + } + }, [searchParams, setSearchParams, submitQuery]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (message.trim()) { + submitQuery(message); + setMessage(""); + } + }; + + return ( +
+
+ {conversation.map((qa) => ( +
+
{qa.question}
+
{qa.answer}
+
+ ))} + {isPreparingAnswer && ( +
+ + Preparing answer… +
+ )} +
+ +
+
+ setMessage(e.target.value)} + placeholder="Ask a question..." + disabled={isGeneratingAnswer} + className="flex-1 rounded-md border border-grid-bright bg-background-dimmed px-3 py-2 text-text-bright placeholder:text-text-dimmed focus:border-indigo-500 focus:outline-none" + /> +
+
+
+ ); +} + +export function KapaChat({ websiteId, onOpen, onClose }: KapaChatProps) { + const [isOpen, setIsOpen] = useState(false); + + const handleOpen = useCallback(() => { + setIsOpen(true); + onOpen?.(); + }, [onOpen]); + + const handleClose = useCallback(() => { + setIsOpen(false); + onClose?.(); + }, [onClose]); + + if (!websiteId) return null; + + return ( + handleOpen(), + onAnswerGenerationCompleted: () => handleOpen(), + }, + }} + > +
+ + + {isOpen && ( +
+
+
+ + Ask AI +
+
+ +
+ )} +
+
+ ); +} diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index 4dab36078e..6bebfc02cc 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -18,7 +18,7 @@ import { Squares2X2Icon, UsersIcon, } from "@heroicons/react/20/solid"; -import { useNavigation } from "@remix-run/react"; +import { useNavigation, useMatches } from "@remix-run/react"; import { useEffect, useRef, useState, type ReactNode } from "react"; import simplur from "simplur"; import { AISparkleIcon } from "~/assets/icons/AISparkleIcon"; @@ -60,7 +60,6 @@ import { v3UsagePath, v3WaitpointTokensPath, } from "~/utils/pathBuilder"; -import { useKapaWidget } from "../../hooks/useKapaWidget"; import { FreePlanUsage } from "../billing/FreePlanUsage"; import { ConnectionIcon, DevPresencePanel, useDevPresence } from "../DevPresence"; import { ImpersonationBanner } from "../ImpersonationBanner"; @@ -84,6 +83,10 @@ import { HelpAndFeedback } from "./HelpAndFeedbackPopover"; import { SideMenuHeader } from "./SideMenuHeader"; import { SideMenuItem } from "./SideMenuItem"; import { SideMenuSection } from "./SideMenuSection"; +import { useFeatures } from "~/hooks/useFeatures"; +import { useTypedMatchesData } from "~/hooks/useTypedMatchData"; +import { type loader } from "~/root"; +import { KapaChat } from "~/components/KapaChat"; type SideMenuUser = Pick & { isImpersonating: boolean }; export type SideMenuProject = Pick< @@ -570,29 +573,24 @@ function SelectorDivider() { } function HelpAndAI() { - const { isKapaEnabled, isKapaOpen, openKapa } = useKapaWidget(); + const matches = useMatches(); + const features = useFeatures(); + const routeMatch = useTypedMatchesData({ + id: "root", + matches, + }); + const isKapaEnabled = features.isManagedCloud && routeMatch?.kapa?.websiteId; return ( <> - - {isKapaEnabled && ( + + {isKapaEnabled && routeMatch.kapa.websiteId && (
- +
void) | { onRender?: () => void } | OpenOptions, - remove?: string | { onRender?: () => void } - ) => void; - } -} - -export function KapaScripts({ websiteId }: { websiteId?: string }) { - if (!websiteId) return null; - - return ( - <> - - } - - - - + + + + + + From d669dda596581b3cfee8ee2ade42f909b38e8e07 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 27 May 2025 17:15:06 +0100 Subject: [PATCH 38/50] remove kapa script --- apps/webapp/app/root.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/webapp/app/root.tsx b/apps/webapp/app/root.tsx index 9c3669ffed..ac556e0f2c 100644 --- a/apps/webapp/app/root.tsx +++ b/apps/webapp/app/root.tsx @@ -106,7 +106,6 @@ export default function App() { - {kapa.websiteId && } From 232de9a65b9c906768e474e3b6bdc9f662fb71d3 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 27 May 2025 18:31:22 +0100 Subject: [PATCH 39/50] Add a delay before the modal opens when usign the URL params --- apps/webapp/app/components/AskAI.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index a2d7fa0b8c..682e86bb61 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -86,7 +86,11 @@ export function AskAIProvider({ children, websiteId }: AskAIProviderProps) { }); const decodedAiHelp = decodeURIComponent(aiHelp); - openAskAI(decodedAiHelp); + + // Add a delay to avoid triggering hCaptcha bot detection + setTimeout(() => { + openAskAI(decodedAiHelp); + }, 1000); } }, [searchParams, setSearchParams, openAskAI]); From b9881911ad7236ae98d4a5a16752076e0225d109 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 27 May 2025 18:31:38 +0100 Subject: [PATCH 40/50] Remove old component --- apps/webapp/app/components/AskAI.tsx | 87 ---------------------------- 1 file changed, 87 deletions(-) diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index 682e86bb61..0eca4cb3d3 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -156,12 +156,6 @@ function AskAIDialog({ initialQuery, isOpen, onOpenChange }: AskAIDialogProps) { ); } -type KapaChatProps = { - websiteId: string; - onOpen?: () => void; - onClose?: () => void; -}; - function ChatMessages({ conversation, isPreparingAnswer, @@ -493,87 +487,6 @@ function ChatInterface({ initialQuery }: { initialQuery?: string }) { ); } -export function AskAI({ websiteId, onOpen }: KapaChatProps) { - const [isOpen, setIsOpen] = useState(false); - const [initialQuery, setInitialQuery] = useState(); - const [searchParams, setSearchParams] = useSearchParams(); - - const handleOpen = useCallback(() => { - setIsOpen(true); - onOpen?.(); - }, [onOpen]); - - // Handle URL param functionality - useEffect(() => { - const aiHelp = searchParams.get("aiHelp"); - if (aiHelp) { - setSearchParams((prev) => { - prev.delete("aiHelp"); - return prev; - }); - - const decodedAiHelp = decodeURIComponent(aiHelp); - setInitialQuery(decodedAiHelp); - handleOpen(); - } - }, [searchParams, setSearchParams, handleOpen]); - - if (!websiteId) return null; - - return ( - handleOpen(), - onAnswerGenerationCompleted: () => handleOpen(), - }, - }} - botProtectionMechanism="hcaptcha" - > -
- - - -
- -
-
- - Ask AI - - -
-
- - - - -
- - Ask AI -
-
- -
-
-
-
- ); -} - function GradientSpinnerBackground({ children, className, From 05f5b4ba79f3cafc11bb4b58e7751a8dac3e33d8 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 27 May 2025 18:31:48 +0100 Subject: [PATCH 41/50] Update to the latest Kapa version --- apps/webapp/package.json | 2 +- pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/webapp/package.json b/apps/webapp/package.json index b613ba8328..4981503fb7 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -55,7 +55,7 @@ "@internal/tracing": "workspace:*", "@internal/zod-worker": "workspace:*", "@internationalized/date": "^3.5.1", - "@kapaai/react-sdk": "^0.1.0", + "@kapaai/react-sdk": "^0.1.2", "@lezer/highlight": "^1.1.6", "@opentelemetry/api": "1.9.0", "@opentelemetry/api-logs": "0.52.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 359d1ee402..72d8ee9868 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -259,8 +259,8 @@ importers: specifier: ^3.5.1 version: 3.5.1 '@kapaai/react-sdk': - specifier: ^0.1.0 - version: 0.1.0(react-dom@18.2.0)(react@18.2.0) + specifier: ^0.1.2 + version: 0.1.2(react-dom@18.2.0)(react@18.2.0) '@lezer/highlight': specifier: ^1.1.6 version: 1.1.6 @@ -7789,8 +7789,8 @@ packages: resolution: {integrity: sha512-Lg3PnLp0QXpxwLIAuuJboLeRaIhrgJjeuh797QADg3xz8wGLugQOS5DpsE8A6i6Adgzf+bacllkKZG3J0tGfDw==} dev: true - /@kapaai/react-sdk@0.1.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-eyEBG5wk1CnlziYFhPjWcn/Lg5+uVGZ8UbSDs7WXqh0Vwc4Ri+iGqVHgdZ3iHt4bLjzHQB5odftlWoRPzek0YA==} + /@kapaai/react-sdk@0.1.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-+FUNmg9sMMqKiNG8UX02C+rLTSFK4pR8Q6GSiBQcA7mtCwo/k1JFRn11eGQnlspGpuyMolmrfqpb0+bmDIGfUA==} peerDependencies: react: '>=17.0.0' react-dom: '>=17.0.0' From 5606509ae29bbd2a53ec39e31d4655856c06459d Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 27 May 2025 18:41:11 +0100 Subject: [PATCH 42/50] Rephrased error message --- apps/webapp/app/components/AskAI.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index 0eca4cb3d3..b5b1a19a3d 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -357,7 +357,7 @@ function ChatMessages({ Error generating answer: - {error} Please try again. If the problem persists, please contact support. + {error} If the problem persists after retrying, please contact support.
From 38cbddec5042b3dbc0c32e50d399436b705cba67 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 12 Jun 2025 12:19:34 +0100 Subject: [PATCH 43/50] Use correct types for conversation --- apps/webapp/app/components/AskAI.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index b5b1a19a3d..2d99a871df 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -5,7 +5,7 @@ import { HandThumbUpIcon, StopIcon, } from "@heroicons/react/20/solid"; -import { KapaProvider, useChat } from "@kapaai/react-sdk"; +import { KapaProvider, QA, useChat } from "@kapaai/react-sdk"; import { useSearchParams } from "@remix-run/react"; import { motion } from "framer-motion"; import { marked } from "marked"; @@ -165,7 +165,7 @@ function ChatMessages({ error, addFeedback, }: { - conversation: any[]; + conversation: QA[]; isPreparingAnswer: boolean; isGeneratingAnswer: boolean; onReset: () => void; From 5374fc46930838de2c554f1dd0ff303eab9ac3c1 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 12 Jun 2025 12:21:17 +0100 Subject: [PATCH 44/50] Fixed types for addFeedback --- apps/webapp/app/components/AskAI.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index 2d99a871df..aa3babc850 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -5,7 +5,7 @@ import { HandThumbUpIcon, StopIcon, } from "@heroicons/react/20/solid"; -import { KapaProvider, QA, useChat } from "@kapaai/react-sdk"; +import { FeedbackComment, KapaProvider, QA, useChat } from "@kapaai/react-sdk"; import { useSearchParams } from "@remix-run/react"; import { motion } from "framer-motion"; import { marked } from "marked"; @@ -171,7 +171,11 @@ function ChatMessages({ onReset: () => void; onExampleClick: (question: string) => void; error: string | null; - addFeedback: (questionAnswerId: string, reaction: "upvote" | "downvote", comment?: any) => void; + addFeedback: ( + questionAnswerId: string, + reaction: "upvote" | "downvote", + comment?: FeedbackComment + ) => void; }) { const [feedbackGivenForQAs, setFeedbackGivenForQAs] = useState>(new Set()); From 038f40e07798f5253afb51dbec1196c5a556ecf2 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 12 Jun 2025 12:33:27 +0100 Subject: [PATCH 45/50] Adds DOMPurify package --- apps/webapp/app/components/AskAI.tsx | 12 ++++++------ apps/webapp/package.json | 2 ++ pnpm-lock.yaml | 27 +++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index aa3babc850..c824a92335 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -5,18 +5,18 @@ import { HandThumbUpIcon, StopIcon, } from "@heroicons/react/20/solid"; -import { FeedbackComment, KapaProvider, QA, useChat } from "@kapaai/react-sdk"; +import { type FeedbackComment, KapaProvider, type QA, useChat } from "@kapaai/react-sdk"; import { useSearchParams } from "@remix-run/react"; import { motion } from "framer-motion"; import { marked } from "marked"; import { + createContext, + type ReactNode, useCallback, + useContext, useEffect, useRef, useState, - createContext, - useContext, - type ReactNode, } from "react"; import { AISparkleIcon } from "~/assets/icons/AISparkleIcon"; import { SparkleListIcon } from "~/assets/icons/SparkleListIcon"; @@ -25,7 +25,6 @@ import { Callout } from "./primitives/Callout"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./primitives/Dialog"; import { Header2 } from "./primitives/Headers"; import { Paragraph } from "./primitives/Paragraph"; -import { ShortcutKey } from "./primitives/ShortcutKey"; import { Spinner } from "./primitives/Spinner"; import { SimpleTooltip, @@ -34,6 +33,7 @@ import { TooltipProvider, TooltipTrigger, } from "./primitives/Tooltip"; +import DOMPurify from "dompurify"; type AskAIContextType = { isOpen: boolean; @@ -257,7 +257,7 @@ function ChatMessages({ {qa.question}
)) diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 5161715b07..efd575445d 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -124,6 +124,7 @@ "cronstrue": "^2.21.0", "cross-env": "^7.0.3", "cuid": "^2.1.8", + "dompurify": "^3.2.6", "dotenv": "^16.4.5", "effect": "^3.11.7", "emails": "workspace:*", @@ -210,6 +211,7 @@ "@types/bcryptjs": "^2.4.2", "@types/compression": "^1.7.2", "@types/cookie": "^0.6.0", + "@types/dompurify": "^3.2.0", "@types/eslint": "^8.4.6", "@types/express": "^4.17.13", "@types/humanize-duration": "^3.27.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 457d9d24d8..444de5c837 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -465,6 +465,9 @@ importers: cuid: specifier: ^2.1.8 version: 2.1.8 + dompurify: + specifier: ^3.2.6 + version: 3.2.6 dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -718,6 +721,9 @@ importers: '@types/cookie': specifier: ^0.6.0 version: 0.6.0 + '@types/dompurify': + specifier: ^3.2.0 + version: 3.2.0 '@types/eslint': specifier: ^8.4.6 version: 8.4.10 @@ -17623,6 +17629,13 @@ packages: '@types/ssh2': 1.15.1 dev: true + /@types/dompurify@3.2.0: + resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==} + deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed. + dependencies: + dompurify: 3.2.6 + dev: true + /@types/email-reply-parser@1.4.2: resolution: {integrity: sha512-kmMoK9WMX4zXf3c0D3tkWHDl0E50V2dv6fVirdTQd/mkvE/Jixh0DZAh3kBgpltr1eaWM3W+kAf4A2c2Z2iU2A==} dev: true @@ -18135,6 +18148,11 @@ packages: resolution: {integrity: sha512-4YIL/2AvvZqKBWenjvEpxpblT2KGO6793ipr5QS7/6DpQ3O3SwZGgNGWezxf3pzeYZc24a2pJIrR/+Jxh/wYNQ==} dev: true + /@types/trusted-types@2.0.7: + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + requiresBuild: true + optional: true + /@types/unist@2.0.6: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} @@ -21747,6 +21765,11 @@ packages: domelementtype: 2.3.0 dev: false + /dompurify@3.2.6: + resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==} + optionalDependencies: + '@types/trusted-types': 2.0.7 + /domutils@3.0.1: resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} dependencies: @@ -24891,6 +24914,7 @@ packages: /ip-address@9.0.5: resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} engines: {node: '>= 12'} + requiresBuild: true dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 @@ -25445,6 +25469,7 @@ packages: /jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + requiresBuild: true dev: false /jsep@1.4.0: @@ -31746,6 +31771,7 @@ packages: /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + requiresBuild: true dev: false /smartwrap@2.0.2: @@ -31997,6 +32023,7 @@ packages: /sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + requiresBuild: true dev: false /sqids@0.3.0: From 6ece9b98978af775001d9f73713317284b44ea72 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 12 Jun 2025 12:35:54 +0100 Subject: [PATCH 46/50] removed unused const --- apps/webapp/app/components/navigation/SideMenu.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index 7233a98124..c6b21396bc 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -585,12 +585,7 @@ function SelectorDivider() { } function HelpAndAI() { - const matches = useMatches(); const features = useFeatures(); - const routeMatch = useTypedMatchesData({ - id: "root", - matches, - }); const { openAskAI, websiteId } = useAskAI(); const isKapaEnabled = features.isManagedCloud && websiteId; From a093f6ecf9cfa0361add2d18d31bedf106e1a382 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 12 Jun 2025 12:39:18 +0100 Subject: [PATCH 47/50] Removed unnecessary platform specification --- apps/webapp/app/components/primitives/Dialog.tsx | 7 +------ apps/webapp/app/components/primitives/SheetV3.tsx | 12 +----------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/apps/webapp/app/components/primitives/Dialog.tsx b/apps/webapp/app/components/primitives/Dialog.tsx index 502d9753c6..5ac179646b 100644 --- a/apps/webapp/app/components/primitives/Dialog.tsx +++ b/apps/webapp/app/components/primitives/Dialog.tsx @@ -53,12 +53,7 @@ const DialogContent = React.forwardRef< diff --git a/apps/webapp/app/components/primitives/SheetV3.tsx b/apps/webapp/app/components/primitives/SheetV3.tsx index 6db7c5853f..bb205f8b42 100644 --- a/apps/webapp/app/components/primitives/SheetV3.tsx +++ b/apps/webapp/app/components/primitives/SheetV3.tsx @@ -91,17 +91,7 @@ const SheetTitle = React.forwardRef< > {children} - + Close From cf91d89ac4b42359e5f8126b4cb55b8e9311f411 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 12 Jun 2025 12:44:09 +0100 Subject: [PATCH 48/50] Reset the timeout when the ai panel pops up Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- apps/webapp/app/components/AskAI.tsx | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index c824a92335..9caff98477 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -80,21 +80,19 @@ export function AskAIProvider({ children, websiteId }: AskAIProviderProps) { useEffect(() => { const aiHelp = searchParams.get("aiHelp"); if (aiHelp) { - setSearchParams((prev) => { - prev.delete("aiHelp"); - return prev; - }); - const decodedAiHelp = decodeURIComponent(aiHelp); - // Add a delay to avoid triggering hCaptcha bot detection - setTimeout(() => { - openAskAI(decodedAiHelp); - }, 1000); - } - }, [searchParams, setSearchParams, openAskAI]); + // Delay to avoid hCaptcha bot detection + const timeoutId = window.setTimeout(() => openAskAI(decodedAiHelp), 1000); - const contextValue: AskAIContextType = { + // Clone instead of mutating in place + const next = new URLSearchParams(searchParams); + next.delete("aiHelp"); + setSearchParams(next); + + return () => clearTimeout(timeoutId); + } + }, [searchParams.toString(), openAskAI]); isOpen, openAskAI, closeAskAI, From 97bb4768f04f6cee208aeb06600656180fcad9e5 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 12 Jun 2025 13:05:23 +0100 Subject: [PATCH 49/50] Fix for coderabbit bad commit --- apps/webapp/app/components/AskAI.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/webapp/app/components/AskAI.tsx b/apps/webapp/app/components/AskAI.tsx index 9caff98477..23b575b771 100644 --- a/apps/webapp/app/components/AskAI.tsx +++ b/apps/webapp/app/components/AskAI.tsx @@ -93,6 +93,8 @@ export function AskAIProvider({ children, websiteId }: AskAIProviderProps) { return () => clearTimeout(timeoutId); } }, [searchParams.toString(), openAskAI]); + + const contextValue: AskAIContextType = { isOpen, openAskAI, closeAskAI, From eda8d6591928de980530a7ccb510825c52876281 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 12 Jun 2025 13:49:47 +0100 Subject: [PATCH 50/50] Clean up imports --- apps/webapp/app/components/navigation/SideMenu.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index c6b21396bc..c1687bac78 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -18,22 +18,21 @@ import { Squares2X2Icon, UsersIcon, } from "@heroicons/react/20/solid"; -import { useMatches, useNavigation } from "@remix-run/react"; +import { useNavigation } from "@remix-run/react"; import { useEffect, useRef, useState, type ReactNode } from "react"; import simplur from "simplur"; +import { AISparkleIcon } from "~/assets/icons/AISparkleIcon"; +import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons"; import { RunsIconExtraSmall } from "~/assets/icons/RunsIcon"; import { TaskIconSmall } from "~/assets/icons/TaskIcon"; import { WaitpointTokenIcon } from "~/assets/icons/WaitpointTokenIcon"; -import { AISparkleIcon } from "~/assets/icons/AISparkleIcon"; import { Avatar } from "~/components/primitives/Avatar"; import { type MatchedEnvironment } from "~/hooks/useEnvironment"; import { useFeatures } from "~/hooks/useFeatures"; import { type MatchedOrganization } from "~/hooks/useOrganizations"; import { type MatchedProject } from "~/hooks/useProject"; -import { useTypedMatchesData } from "~/hooks/useTypedMatchData"; import { useHasAdminAccess } from "~/hooks/useUser"; import { type User } from "~/models/user.server"; -import { type loader } from "~/root"; import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route"; import { type FeedbackType } from "~/routes/resources.feedback"; import { IncidentStatusPanel } from "~/routes/resources.incidents"; @@ -64,6 +63,7 @@ import { v3UsagePath, v3WaitpointTokensPath, } from "~/utils/pathBuilder"; +import { useAskAI } from "../AskAI"; import { FreePlanUsage } from "../billing/FreePlanUsage"; import { ConnectionIcon, DevPresencePanel, useDevPresence } from "../DevPresence"; import { ImpersonationBanner } from "../ImpersonationBanner"; @@ -82,14 +82,12 @@ import { TextLink } from "../primitives/TextLink"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip"; import { ShortcutsAutoOpen } from "../Shortcuts"; import { UserProfilePhoto } from "../UserProfilePhoto"; -import { useAskAI } from "../AskAI"; +import { V4Badge } from "../V4Badge"; import { EnvironmentSelector } from "./EnvironmentSelector"; import { HelpAndFeedback } from "./HelpAndFeedbackPopover"; import { SideMenuHeader } from "./SideMenuHeader"; import { SideMenuItem } from "./SideMenuItem"; import { SideMenuSection } from "./SideMenuSection"; -import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons"; -import { V4Badge } from "../V4Badge"; type SideMenuUser = Pick & { isImpersonating: boolean }; export type SideMenuProject = Pick<