Skip to content

Use the Kapa AI SDK instead of the Kapa AI widget #2113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 53 commits into from
Jun 12, 2025
Merged

Use the Kapa AI SDK instead of the Kapa AI widget #2113

merged 53 commits into from
Jun 12, 2025

Conversation

samejr
Copy link
Member

@samejr samejr commented May 28, 2025

This replaces the Kapa AI Javascript widget with their React SDK. This means:

  • It's fully skinned in our style
  • Still works with when triggered via a URL param
  • You can vote on answers which displays in the kapa dashboard
  • You can use the useAskAI hook to trigger it from anywhere in the app and pass it a payload 'answer'
  • Uses hCaptcha instead of reCaptcha (so now more floating captcha UI in the bottom right!)

Testing for Test Cloud

  • It uses a setTimeout delay when opening the DIalog to ensure hCaptcha doesn't think it's a bot. Make sure this works reliably
  • Test hCaptcha works reliability during normal use
  • Test votes come through to the kapa dashboard
  • Test shortcuts work
askai.mp4

Copy link

changeset-bot bot commented May 28, 2025

⚠️ No Changeset found

Latest commit: 7518121

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented May 28, 2025

Walkthrough

A new AI chat system named "AskAI" was integrated into the web application, replacing the previous Kapa AI widget implementation. This involved adding a comprehensive React-based chat interface with context management, modal dialog UI, chat history, feedback mechanisms, and bot protection via hCaptcha. Supporting components and hooks for AskAI were introduced, while the old Kapa widget integration and its related hooks were removed. The navigation and provider structure were updated to use the new AskAI context. Additionally, a new SVG icon component and animated gradient-glow CSS utility were added, and relevant dependencies such as @kapaai/react-sdk and dompurify were included in the package configuration. Minor UI refinements were made to dialog and shortcut key components.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eda8d65 and 7518121.

📒 Files selected for processing (2)
  • apps/webapp/app/components/navigation/SideMenu.tsx (5 hunks)
  • apps/webapp/package.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/webapp/package.json
  • apps/webapp/app/components/navigation/SideMenu.tsx
⏰ Context from checks skipped due to timeout of 90000ms (25)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (9, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (10, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 10)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 10)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
apps/webapp/app/components/AskAI.tsx (3)

91-93: Consider making the hCaptcha delay configurable.

The 1000ms delay is hardcoded. Consider making this configurable through props or environment variables to allow for adjustment based on different environments or testing needs.

+const HCAPTCHA_DELAY_MS = process.env.HCAPTCHA_DELAY_MS || 1000;

      // Add a delay to avoid triggering hCaptcha bot detection
      setTimeout(() => {
        openAskAI(decodedAiHelp);
-      }, 1000);
+      }, HCAPTCHA_DELAY_MS);

306-310: Extract duplicate feedback logic into a helper function.

The feedback handling logic is duplicated for upvote and downvote actions.

+                const handleFeedback = (reaction: "upvote" | "downvote") => {
+                  const latestQA = conversation[conversation.length - 1];
+                  if (latestQA?.id) {
+                    addFeedback(latestQA.id, reaction);
+                    setFeedbackGivenForQAs((prev) => new Set(prev).add(latestQA.id));
+                  }
+                };
+
                 <div className="flex items-center">
                   <Button
                     variant="minimal/small"
-                    onClick={() => {
-                      const latestQA = conversation[conversation.length - 1];
-                      if (latestQA?.id) {
-                        addFeedback(latestQA.id, "upvote");
-                        setFeedbackGivenForQAs((prev) => new Set(prev).add(latestQA.id));
-                      }
-                    }}
+                    onClick={() => handleFeedback("upvote")}
                     className="size-8 px-1.5"
                   >
                     <HandThumbUpIcon className="size-4 text-text-dimmed transition group-hover/button:text-success" />
                   </Button>
                   <Button
                     variant="minimal/small"
-                    onClick={() => {
-                      const latestQA = conversation[conversation.length - 1];
-                      if (latestQA?.id) {
-                        addFeedback(latestQA.id, "downvote");
-                        setFeedbackGivenForQAs((prev) => new Set(prev).add(latestQA.id));
-                      }
-                    }}
+                    onClick={() => handleFeedback("downvote")}
                     className="size-8 px-1.5"
                   >

Also applies to: 319-323


539-539: Remove the extra empty line at the end of the file.

-
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 021d6d8 and 5606509.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • apps/webapp/app/assets/icons/SparkleListIcon.tsx (1 hunks)
  • apps/webapp/app/components/AskAI.tsx (1 hunks)
  • apps/webapp/app/components/navigation/SideMenu.tsx (4 hunks)
  • apps/webapp/app/components/primitives/Dialog.tsx (1 hunks)
  • apps/webapp/app/components/primitives/SheetV3.tsx (1 hunks)
  • apps/webapp/app/components/primitives/ShortcutKey.tsx (1 hunks)
  • apps/webapp/app/hooks/useKapaWidget.tsx (0 hunks)
  • apps/webapp/app/root.tsx (2 hunks)
  • apps/webapp/app/tailwind.css (2 hunks)
  • apps/webapp/package.json (4 hunks)
💤 Files with no reviewable changes (1)
  • apps/webapp/app/hooks/useKapaWidget.tsx
🧰 Additional context used
🧬 Code Graph Analysis (5)
apps/webapp/app/components/primitives/SheetV3.tsx (1)
apps/webapp/app/components/primitives/ShortcutKey.tsx (1)
  • ShortcutKey (38-55)
apps/webapp/app/components/primitives/ShortcutKey.tsx (1)
apps/webapp/app/utils/cn.ts (1)
  • cn (30-32)
apps/webapp/app/root.tsx (1)
apps/webapp/app/components/AskAI.tsx (1)
  • AskAIProvider (60-125)
apps/webapp/app/components/primitives/Dialog.tsx (1)
apps/webapp/app/components/primitives/ShortcutKey.tsx (1)
  • ShortcutKey (38-55)
apps/webapp/app/components/navigation/SideMenu.tsx (4)
apps/webapp/app/hooks/useFeatures.ts (1)
  • useFeatures (5-9)
apps/webapp/app/root.tsx (1)
  • loader (42-64)
apps/webapp/app/components/AskAI.tsx (1)
  • useAskAI (47-53)
apps/webapp/app/components/navigation/HelpAndFeedbackPopover.tsx (1)
  • HelpAndFeedback (26-191)
🪛 Biome (1.9.4)
apps/webapp/app/components/AskAI.tsx

[error] 256-256: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (23)
apps/webapp/app/assets/icons/SparkleListIcon.tsx (1)

1-14: LGTM! Clean and well-structured icon component.

The implementation follows React best practices with proper TypeScript typing, optional className prop for styling flexibility, and uses currentColor for theme compatibility. The SVG structure is well-formed with appropriate paths and rectangles.

apps/webapp/app/tailwind.css (2)

8-12: LGTM! Proper CSS custom property definition.

The @property syntax is correctly used to define the --gradient-angle custom property with appropriate syntax, initial value, and inheritance behavior.


49-83: LGTM! Well-implemented animated gradient utility.

The animated gradient glow utility is well-structured with:

  • Proper positioning and overflow handling
  • Efficient pseudo-element approach for the background effect
  • Smooth conic-gradient animation with reasonable performance characteristics
  • Appropriate use of pointer-events: none to prevent interaction issues
  • Good balance of blur and opacity for the glow effect
apps/webapp/app/components/primitives/SheetV3.tsx (1)

94-104: LGTM! Platform-specific shortcut structure improves consistency.

The update to use explicit windows and mac shortcut definitions enhances consistency with the ShortcutKey component's capabilities, even though both platforms use the same "esc" key in this case. This provides better clarity and maintains consistency with other shortcut usage patterns.

apps/webapp/app/components/primitives/ShortcutKey.tsx (2)

16-16: LGTM! Improved semantic color naming for small variant.

The change from border-dimmed/40 to border-text-dimmed/40 (and corresponding hover effects) improves semantic naming and maintains visual consistency.


17-17: LGTM! Proper use of utility function for class composition.

Using the cn utility to combine the base medium styles with the additional hover effect (group-hover:border-charcoal-550) is the correct approach for composing Tailwind CSS classes while handling potential conflicts.

apps/webapp/app/root.tsx (3)

3-3: Clean import optimization.

Good removal of unused imports (useMatches, useTypedMatchesData, KapaScripts) as part of the migration from the old Kapa widget to the new AskAI system.


9-9: Proper integration of AskAIProvider.

The import aligns with the new AI chat system implementation.


111-116: Well-structured provider integration.

The AskAIProvider is properly positioned at the root level and correctly receives the websiteId from the loader data. The conditional || null ensures type safety when kapa.websiteId is undefined. The nested component structure maintains the existing hierarchy while providing the new AI context.

apps/webapp/app/components/primitives/Dialog.tsx (1)

53-67: Excellent UI improvements to the dialog close button.

The refactoring brings several enhancements:

  • Simplified structure by removing unnecessary wrapper div
  • Better hover coordination using group and group-hover: classes
  • More explicit shortcut key definition with platform-specific variants
  • Improved positioning and spacing with right-2 top-[0.5625rem]
  • Enhanced visual feedback with transition effects on the icon

The change from variant="small" to variant="medium" for ShortcutKey aligns with the broader UI consistency improvements mentioned in the AI summary.

apps/webapp/package.json (2)

58-58: Essential dependency for AskAI integration.

The addition of @kapaai/react-sdk is necessary for the new AI chat interface that replaces the previous Kapa widget integration. The version ^0.1.2 appears reasonable for this new integration.


49-61: Good dependency organization.

The reordering of dependencies like @internal/redis, @internal/tracing, @trigger.dev/redis-worker, and @opentelemetry/api-logs improves maintainability without changing functionality.

Also applies to: 108-108

apps/webapp/app/components/navigation/SideMenu.tsx (4)

21-21: Clean migration from useKapaWidget to useAskAI.

The import changes properly reflect the transition from the old Kapa widget integration to the new AskAI system. The addition of useMatches, useFeatures, useTypedMatchesData, and useAskAI provides the necessary hooks for the refactored component logic.

Also applies to: 27-27, 30-30, 33-33, 36-36, 84-84


576-583: Well-structured data fetching for AI feature detection.

The refactored approach properly:

  • Uses useMatches and useTypedMatchesData to access root loader data
  • Leverages useFeatures for feature flag detection
  • Extracts websiteId and openAskAI from the new useAskAI hook
  • Maintains the same isKapaEnabled logic using features.isManagedCloud && websiteId

This approach is more explicit and maintainable than the previous implementation.


588-588: Simplified HelpAndFeedback usage.

The removal of the disableShortcut prop suggests that shortcut handling is now managed at a higher level or within the component itself, which aligns with the overall UI improvements in this PR.


600-600: Streamlined onClick handler.

The simplification from a block function calling openKapa() to a direct call to openAskAI() reflects the cleaner API of the new AskAI system.

apps/webapp/app/components/AskAI.tsx (7)

1-36: LGTM!

The imports are well-organized and appropriate for the functionality implemented.


38-53: LGTM!

The context setup follows React best practices with proper typing and error handling.


113-116: Verify the KapaProvider callback implementation.

The callbacks onQuerySubmit and onAnswerGenerationCompleted both call openAskAI() without parameters, which only sets initialQuery to undefined. This seems redundant since the dialog is already open when these callbacks fire.

Please verify if these callbacks are intended to perform specific actions or if they can be removed.


127-157: LGTM!

The dialog component is well-structured with proper state management and styling.


380-488: LGTM!

The ChatInterface component is well-implemented with proper state management, form handling, and animation effects.


490-512: LGTM!

The GradientSpinnerBackground component provides a nice visual effect for the loading states.


514-538: LGTM!

The AskAIButton component is simple and effective, with proper tooltip integration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/webapp/app/components/AskAI.tsx (2)

255-260: Potentially unstable React keys when qa.id is undefined

key={qa.id || \temp-${qa.question}`}may collide if a user submits the same question twice before IDs are returned, leading to incorrect list reconciliation. Generate a truly unique fallback (e.g.crypto.randomUUID()` or an incrementing ref).

-<div key={qa.id || `temp-${qa.question}`} className="mb-4">
+<div key={qa.id ?? crypto.randomUUID()} className="mb-4">
🧰 Tools
🪛 Biome (1.9.4)

[error] 260-260: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


259-261: Repeated re-parsing/ sanitising of markdown can be expensive

marked + DOMPurify.sanitize runs on every render.
For long conversations this can become costly. Memoise the HTML per QA item:

const html = useMemo(
  () => DOMPurify.sanitize(marked(qa.answer)),
  [qa.answer]
);

Wrap it in a component or compute once before map.

🧰 Tools
🪛 Biome (1.9.4)

[error] 260-260: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ddce5f and 038f40e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • apps/webapp/app/components/AskAI.tsx (1 hunks)
  • apps/webapp/package.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/webapp/package.json
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/webapp/app/components/AskAI.tsx (5)
apps/webapp/app/components/primitives/Dialog.tsx (4)
  • Dialog (114-114)
  • DialogContent (116-116)
  • DialogHeader (117-117)
  • DialogTitle (119-119)
apps/webapp/app/assets/icons/AISparkleIcon.tsx (1)
  • AISparkleIcon (1-31)
apps/webapp/app/components/primitives/Buttons.tsx (1)
  • Button (294-329)
apps/webapp/app/components/primitives/Spinner.tsx (1)
  • Spinner (8-62)
apps/webapp/app/components/primitives/Tooltip.tsx (5)
  • SimpleTooltip (118-118)
  • TooltipProvider (118-118)
  • Tooltip (118-118)
  • TooltipTrigger (118-118)
  • TooltipContent (118-118)
🪛 Biome (1.9.4)
apps/webapp/app/components/AskAI.tsx

[error] 260-260: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms (25)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 10)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (9, 10)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 10)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (10, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 10)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
apps/webapp/app/components/AskAI.tsx (1)

104-107: openAskAI() becomes a no-op when websiteId is null

When websiteId is absent you short-circuit the JSX and don’t render AskAIDialog, yet openAskAI will still set isOpen to true.
Call sites might assume a dialog will appear and wait for callbacks that never come. Consider either:

  1. Throwing early in openAskAI when websiteId is null, or
  2. Always rendering the dialog but displaying a “feature unavailable” state.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/webapp/app/components/AskAI.tsx (2)

256-259: Consider replacing dangerouslySetInnerHTML or silencing the linter

Even though the HTML is being sanitised with DOMPurify, dangerouslySetInnerHTML still triggers lint/security/noDangerouslySetInnerHtml. If keeping the current approach:

  1. Explicitly disable the rule for this line with an inline comment, or
  2. Switch to react-markdown / markdown-to-jsx to avoid dangerouslySetInnerHTML altogether.

Either way you get rid of the noisy Biome warning.

🧰 Tools
🪛 Biome (1.9.4)

[error] 258-258: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


191-196: Hoist exampleQuestions outside the component

exampleQuestions is recreated on every re-render even though it is static. Move it to file scope (or wrap in useMemo) to avoid needless allocations.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 038f40e and cf91d89.

📒 Files selected for processing (3)
  • apps/webapp/app/components/AskAI.tsx (1 hunks)
  • apps/webapp/app/components/navigation/SideMenu.tsx (4 hunks)
  • apps/webapp/app/components/primitives/Dialog.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Dialog.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
apps/webapp/app/components/AskAI.tsx

[error] 100-100: Expected an expression but instead found '}'.

Expected an expression here.

(parse)


[error] 103-103: Illegal return statement outside of a function

(parse)


[error] 106-122: Illegal return statement outside of a function

(parse)


[error] 122-123: Expected a statement but instead found '}'.

Expected a statement here.

(parse)


[error] 258-258: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🪛 GitHub Actions: 🤖 PR Checks
apps/webapp/app/components/AskAI.tsx

[error] 100-100: TypeScript error TS1109: Expression expected.

Comment on lines +96 to +100
isOpen,
openAskAI,
closeAskAI,
websiteId,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix missing contextValue declaration – the file does not compile

contextValue is referenced twice below but never declared. This leaves five stray identifiers in the top-level scope and is the root cause of the TS/parse errors reported by the pipeline.

-    isOpen,
-    openAskAI,
-    closeAskAI,
-    websiteId,
-  };
+  const contextValue: AskAIContextType = {
+    isOpen,
+    openAskAI,
+    closeAskAI,
+    websiteId,
+  };

Without this object the component fails to build and every downstream reference to contextValue is invalid.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
isOpen,
openAskAI,
closeAskAI,
websiteId,
};
const contextValue: AskAIContextType = {
isOpen,
openAskAI,
closeAskAI,
websiteId,
};
🧰 Tools
🪛 Biome (1.9.4)

[error] 100-100: Expected an expression but instead found '}'.

Expected an expression here.

(parse)

🪛 GitHub Actions: 🤖 PR Checks

[error] 100-100: TypeScript error TS1109: Expression expected.

🤖 Prompt for AI Agents
In apps/webapp/app/components/AskAI.tsx around lines 96 to 100, the variable
contextValue is used but not declared, causing TypeScript and parsing errors. To
fix this, declare contextValue as an object that includes the relevant
properties such as isOpen, openAskAI, closeAskAI, and websiteId before it is
referenced. This will ensure the component compiles correctly and all references
to contextValue are valid.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
apps/webapp/app/components/AskAI.tsx (4)

80-95: useEffect dependency can still trigger unnecessary re-renders

searchParams.toString() is recalculated on every render even when the query
string hasn’t actually changed, so the effect may re-run more often than
needed. A cheaper and more explicit option is to watch the specific parameter
you care about:

-useEffect(() => {
-  const aiHelp = searchParams.get("aiHelp");
-  ...
-}, [searchParams.toString(), openAskAI]);
+useEffect(() => {
+  const aiHelp = searchParams.get("aiHelp");
+  ...
+// Only re-run when the *value* of the param changes
+}, [searchParams.get("aiHelp"), openAskAI]);

This avoids the stringification cost and prevents the effect from firing when
unrelated query-string keys change.


259-261: Sanitisation looks good, but tighten the allowed tags to silence the linter

DOMPurify is already in place – nice!
To satisfy the security linter and further reduce attack-surface you can pass a
minimal allowed-tags list:

- dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(marked(qa.answer)) }}
+ dangerouslySetInnerHTML={{
+   __html: DOMPurify.sanitize(marked(qa.answer), { ALLOWED_TAGS: ["p","ul","ol","li","a","code","pre","strong","em"] })
+ }}

Keeps flexibility while nullifying most XSS vectors and removes the Biome
warning.

🧰 Tools
🪛 Biome (1.9.4)

[error] 260-260: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


221-252: Make example buttons non-submit & prevent potential form side-effects

These <motion.button> elements live outside a <form> now, but future
refactors could nest them unintentionally. Adding an explicit type="button"
guards against accidental form submission:

-<motion.button
+<motion.button
+  type="button"

308-329: Set-state helper mutates the new Set unnecessarily

new Set(prev).add(id) mutates the newly created set inline. While harmless,
it’s clearer to mutate in a separate step:

-setFeedbackGivenForQAs((prev) => new Set(prev).add(latestQA.id));
+setFeedbackGivenForQAs((prev) => {
+  const next = new Set(prev);
+  next.add(latestQA.id);
+  return next;
+});

Improves readability and prevents accidental reliance on Set.add’s return
value.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf91d89 and 97bb476.

📒 Files selected for processing (1)
  • apps/webapp/app/components/AskAI.tsx (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
apps/webapp/app/components/AskAI.tsx

[error] 260-260: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms (16)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 10)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 10)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 10)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: Analyze (javascript-typescript)

Comment on lines +456 to +465
<span
onClick={() => stopGeneration()}
className="group relative z-10 flex size-10 min-w-10 cursor-pointer items-center justify-center"
>
<StopIcon className="z-10 size-5 text-indigo-500 transition group-hover:text-indigo-400" />
<GradientSpinnerBackground
className="absolute inset-0 animate-spin"
hoverEffect
/>
</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace non-semantic <span> with an accessible button

The “stop generating” control is currently a clickable <span> which is not
keyboard-focusable and fails accessibility guidelines. Swapping to a <button>
restores correct semantics, keyboard support and native focus styling:

-<span
-  onClick={() => stopGeneration()}
-  className="group relative z-10 flex size-10 min-w-10 cursor-pointer items-center justify-center"
->
+<button
+  type="button"
+  onClick={stopGeneration}
+  aria-label="Stop generating"
+  className="group relative z-10 flex size-10 min-w-10 items-center justify-center rounded-full focus-visible:focus-custom"
+>
   <StopIcon className="z-10 size-5 text-indigo-500 transition group-hover:text-indigo-400" />
   <GradientSpinnerBackground
     className="absolute inset-0 animate-spin"
     hoverEffect
   />
-</span>
+</button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span
onClick={() => stopGeneration()}
className="group relative z-10 flex size-10 min-w-10 cursor-pointer items-center justify-center"
>
<StopIcon className="z-10 size-5 text-indigo-500 transition group-hover:text-indigo-400" />
<GradientSpinnerBackground
className="absolute inset-0 animate-spin"
hoverEffect
/>
</span>
<button
type="button"
onClick={stopGeneration}
aria-label="Stop generating"
className="group relative z-10 flex size-10 min-w-10 items-center justify-center rounded-full focus-visible:focus-custom"
>
<StopIcon className="z-10 size-5 text-indigo-500 transition group-hover:text-indigo-400" />
<GradientSpinnerBackground
className="absolute inset-0 animate-spin"
hoverEffect
/>
</button>
🤖 Prompt for AI Agents
In apps/webapp/app/components/AskAI.tsx around lines 456 to 465, replace the
non-semantic clickable <span> element used for the "stop generating" control
with a <button> element to improve accessibility. This change will make the
control keyboard-focusable, provide correct semantic meaning, and restore native
focus styling. Ensure the onClick handler and all class names are preserved on
the new <button> element.

samejr added 2 commits June 12, 2025 13:49
# Conflicts:
#	apps/webapp/app/components/navigation/SideMenu.tsx
@samejr samejr merged commit e12c82b into main Jun 12, 2025
33 checks passed
@samejr samejr deleted the kapa-ai-sdk branch June 12, 2025 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants