Skip to content

Commit 69de253

Browse files
committed
Move all the logic into a single component, preparing for client only
1 parent 20a8e7c commit 69de253

File tree

3 files changed

+66
-119
lines changed

3 files changed

+66
-119
lines changed

apps/webapp/app/components/AskAI.tsx

Lines changed: 60 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,21 @@ import {
77
} from "@heroicons/react/20/solid";
88
import { type FeedbackComment, KapaProvider, type QA, useChat } from "@kapaai/react-sdk";
99
import { useSearchParams } from "@remix-run/react";
10+
import DOMPurify from "dompurify";
1011
import { motion } from "framer-motion";
1112
import { marked } from "marked";
12-
import {
13-
createContext,
14-
type ReactNode,
15-
useCallback,
16-
useContext,
17-
useEffect,
18-
useRef,
19-
useState,
20-
} from "react";
13+
import { useCallback, useEffect, useRef, useState } from "react";
14+
import { useTypedRouteLoaderData } from "remix-typedjson";
2115
import { AISparkleIcon } from "~/assets/icons/AISparkleIcon";
2216
import { SparkleListIcon } from "~/assets/icons/SparkleListIcon";
17+
import { useFeatures } from "~/hooks/useFeatures";
18+
import { type loader } from "~/root";
2319
import { Button } from "./primitives/Buttons";
2420
import { Callout } from "./primitives/Callout";
2521
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./primitives/Dialog";
2622
import { Header2 } from "./primitives/Headers";
2723
import { Paragraph } from "./primitives/Paragraph";
24+
import { ShortcutKey } from "./primitives/ShortcutKey";
2825
import { Spinner } from "./primitives/Spinner";
2926
import {
3027
SimpleTooltip,
@@ -33,31 +30,28 @@ import {
3330
TooltipProvider,
3431
TooltipTrigger,
3532
} from "./primitives/Tooltip";
36-
import DOMPurify from "dompurify";
3733

38-
type AskAIContextType = {
39-
isOpen: boolean;
40-
openAskAI: (question?: string) => void;
41-
closeAskAI: () => void;
42-
websiteId: string | null;
43-
};
34+
function useKapaWebsiteId() {
35+
const routeMatch = useTypedRouteLoaderData<typeof loader>("root");
36+
return routeMatch?.kapa.websiteId;
37+
}
4438

45-
const AskAIContext = createContext<AskAIContextType | null>(null);
39+
export function AskAI() {
40+
const { isManagedCloud } = useFeatures();
41+
const websiteId = useKapaWebsiteId();
4642

47-
export function useAskAI() {
48-
const context = useContext(AskAIContext);
49-
if (!context) {
50-
throw new Error("useAskAI must be used within an AskAIProvider");
43+
if (!isManagedCloud || !websiteId) {
44+
return null;
5145
}
52-
return context;
46+
47+
return <AskAIProvider websiteId={websiteId} />;
5348
}
5449

5550
type AskAIProviderProps = {
56-
children: ReactNode;
57-
websiteId: string | null;
51+
websiteId: string;
5852
};
5953

60-
export function AskAIProvider({ children, websiteId }: AskAIProviderProps) {
54+
function AskAIProvider({ websiteId }: AskAIProviderProps) {
6155
const [isOpen, setIsOpen] = useState(false);
6256
const [initialQuery, setInitialQuery] = useState<string | undefined>();
6357
const [searchParams, setSearchParams] = useSearchParams();
@@ -94,45 +88,57 @@ export function AskAIProvider({ children, websiteId }: AskAIProviderProps) {
9488
}
9589
}, [searchParams.toString(), openAskAI]);
9690

97-
const contextValue: AskAIContextType = {
98-
isOpen,
99-
openAskAI,
100-
closeAskAI,
101-
websiteId,
102-
};
103-
104-
if (!websiteId) {
105-
return <AskAIContext.Provider value={contextValue}>{children}</AskAIContext.Provider>;
106-
}
107-
10891
return (
109-
<AskAIContext.Provider value={contextValue}>
110-
<KapaProvider
111-
integrationId={websiteId}
112-
callbacks={{
113-
askAI: {
114-
onQuerySubmit: () => openAskAI(),
115-
onAnswerGenerationCompleted: () => openAskAI(),
116-
},
117-
}}
118-
botProtectionMechanism="hcaptcha"
119-
>
120-
{children}
121-
<AskAIDialog initialQuery={initialQuery} isOpen={isOpen} onOpenChange={setIsOpen} />
122-
</KapaProvider>
123-
</AskAIContext.Provider>
92+
<KapaProvider
93+
integrationId={websiteId}
94+
callbacks={{
95+
askAI: {
96+
onQuerySubmit: () => openAskAI(),
97+
onAnswerGenerationCompleted: () => openAskAI(),
98+
},
99+
}}
100+
botProtectionMechanism="hcaptcha"
101+
>
102+
<TooltipProvider disableHoverableContent>
103+
<Tooltip>
104+
<TooltipTrigger asChild>
105+
<div className="inline-flex">
106+
<Button
107+
variant="small-menu-item"
108+
data-action="ask-ai"
109+
shortcut={{ modifiers: ["mod"], key: "/", enabledOnInputElements: true }}
110+
hideShortcutKey
111+
data-modal-override-open-class-ask-ai="true"
112+
onClick={() => openAskAI()}
113+
>
114+
<AISparkleIcon className="size-5" />
115+
</Button>
116+
</div>
117+
</TooltipTrigger>
118+
<TooltipContent side="top" className="flex items-center gap-1 py-1.5 pl-2.5 pr-2 text-xs">
119+
Ask AI
120+
<ShortcutKey shortcut={{ modifiers: ["mod"], key: "/" }} variant="medium/bright" />
121+
</TooltipContent>
122+
</Tooltip>
123+
</TooltipProvider>
124+
<AskAIDialog
125+
initialQuery={initialQuery}
126+
isOpen={isOpen}
127+
onOpenChange={setIsOpen}
128+
closeAskAI={closeAskAI}
129+
/>
130+
</KapaProvider>
124131
);
125132
}
126133

127134
type AskAIDialogProps = {
128135
initialQuery?: string;
129136
isOpen: boolean;
130137
onOpenChange: (open: boolean) => void;
138+
closeAskAI: () => void;
131139
};
132140

133-
function AskAIDialog({ initialQuery, isOpen, onOpenChange }: AskAIDialogProps) {
134-
const { closeAskAI } = useAskAI();
135-
141+
function AskAIDialog({ initialQuery, isOpen, onOpenChange, closeAskAI }: AskAIDialogProps) {
136142
const handleOpenChange = (open: boolean) => {
137143
if (!open) {
138144
closeAskAI();
@@ -514,29 +520,3 @@ function GradientSpinnerBackground({
514520
</div>
515521
);
516522
}
517-
518-
export function AskAIButton({ question }: { question?: string }) {
519-
const { openAskAI } = useAskAI();
520-
521-
return (
522-
<TooltipProvider disableHoverableContent>
523-
<Tooltip>
524-
<TooltipTrigger asChild>
525-
<div className="inline-flex">
526-
<Button
527-
variant="minimal/small"
528-
onClick={() => openAskAI(question)}
529-
className="pl-0.5 pr-1"
530-
data-action="ask-ai"
531-
>
532-
<AISparkleIcon className="size-5" />
533-
</Button>
534-
</div>
535-
</TooltipTrigger>
536-
<TooltipContent side="top" className="flex items-center gap-1 px-2 py-1.5 text-xs">
537-
Ask AI
538-
</TooltipContent>
539-
</Tooltip>
540-
</TooltipProvider>
541-
);
542-
}

apps/webapp/app/components/navigation/SideMenu.tsx

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
import { useNavigation } from "@remix-run/react";
2222
import { useEffect, useRef, useState, type ReactNode } from "react";
2323
import simplur from "simplur";
24-
import { AISparkleIcon } from "~/assets/icons/AISparkleIcon";
2524
import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons";
2625
import { RunsIconExtraSmall } from "~/assets/icons/RunsIcon";
2726
import { TaskIconSmall } from "~/assets/icons/TaskIcon";
@@ -63,7 +62,7 @@ import {
6362
v3UsagePath,
6463
v3WaitpointTokensPath,
6564
} from "~/utils/pathBuilder";
66-
import { useAskAI } from "../AskAI";
65+
import { AskAI } from "../AskAI";
6766
import { FreePlanUsage } from "../billing/FreePlanUsage";
6867
import { ConnectionIcon, DevPresencePanel, useDevPresence } from "../DevPresence";
6968
import { ImpersonationBanner } from "../ImpersonationBanner";
@@ -77,7 +76,6 @@ import {
7776
PopoverMenuItem,
7877
PopoverTrigger,
7978
} from "../primitives/Popover";
80-
import { ShortcutKey } from "../primitives/ShortcutKey";
8179
import { TextLink } from "../primitives/TextLink";
8280
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip";
8381
import { ShortcutsAutoOpen } from "../Shortcuts";
@@ -587,40 +585,12 @@ function SelectorDivider() {
587585

588586
function HelpAndAI() {
589587
const features = useFeatures();
590-
const { openAskAI, websiteId } = useAskAI();
591-
const isKapaEnabled = features.isManagedCloud && websiteId;
592588

593589
return (
594590
<>
595591
<ShortcutsAutoOpen />
596592
<HelpAndFeedback />
597-
{isKapaEnabled && (
598-
<TooltipProvider disableHoverableContent>
599-
<Tooltip>
600-
<TooltipTrigger asChild>
601-
<div className="inline-flex">
602-
<Button
603-
variant="small-menu-item"
604-
data-action="ask-ai"
605-
shortcut={{ modifiers: ["mod"], key: "/", enabledOnInputElements: true }}
606-
hideShortcutKey
607-
data-modal-override-open-class-ask-ai="true"
608-
onClick={() => openAskAI()}
609-
>
610-
<AISparkleIcon className="size-5" />
611-
</Button>
612-
</div>
613-
</TooltipTrigger>
614-
<TooltipContent
615-
side="top"
616-
className="flex items-center gap-1 py-1.5 pl-2.5 pr-2 text-xs"
617-
>
618-
Ask AI
619-
<ShortcutKey shortcut={{ modifiers: ["mod"], key: "/" }} variant="medium/bright" />
620-
</TooltipContent>
621-
</Tooltip>
622-
</TooltipProvider>
623-
)}
593+
<AskAI />
624594
</>
625595
);
626596
}

apps/webapp/app/root.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { ExternalScripts } from "remix-utils/external-scripts";
66
import type { ToastMessage } from "~/models/message.server";
77
import { commitSession, getSession } from "~/models/message.server";
88
import tailwindStylesheetUrl from "~/tailwind.css";
9-
import { AskAIProvider } from "./components/AskAI";
109
import { RouteErrorDisplay } from "./components/ErrorDisplay";
1110
import { AppContainer, MainCenteredContainer } from "./components/layout/AppLayout";
1211
import { ShortcutsProvider } from "./components/primitives/ShortcutsProvider";
@@ -108,12 +107,10 @@ export default function App() {
108107
<Links />
109108
</head>
110109
<body className="h-full overflow-hidden bg-background-dimmed">
111-
<AskAIProvider websiteId={kapa.websiteId || null}>
112-
<ShortcutsProvider>
113-
<Outlet />
114-
<Toast />
115-
</ShortcutsProvider>
116-
</AskAIProvider>
110+
<ShortcutsProvider>
111+
<Outlet />
112+
<Toast />
113+
</ShortcutsProvider>
117114
<ScrollRestoration />
118115
<ExternalScripts />
119116
<Scripts />

0 commit comments

Comments
 (0)