diff --git a/frontend/components/ChatInputField.tsx b/frontend/components/ChatInputField.tsx
index f389fa5..60c9682 100644
--- a/frontend/components/ChatInputField.tsx
+++ b/frontend/components/ChatInputField.tsx
@@ -72,6 +72,7 @@ import {
DropdownMenuTrigger,
} from "@/frontend/components/ui/dropdown-menu";
import { FaTools } from "react-icons/fa";
+import { ConditionalTooltip } from "./ui/conditional-tooltip";
// Extended UIMessage type to include attachments
type ExtendedUIMessage = UIMessage & {
@@ -1419,31 +1420,36 @@ function PureInputField({
{/* Enhance Button */}
{!isImageGenMode && input.trim().length > 0 && !isGuest && (
-
+
+
)}
{/* Voice Input Button */}
@@ -1524,16 +1530,23 @@ function PureInputField({
onOpenChange={setIsToolsDropdownOpen}
>
-
+
+
{
// Filter out empty strings and only keep valid HTTP(S) URLs
- return typeof img === "string" && img.trim().length > 0 && /^https?:\/\//.test(img.trim());
+ return (
+ typeof img === "string" &&
+ img.trim().length > 0 &&
+ /^https?:\/\//.test(img.trim())
+ );
})
.slice(0, 15);
return images;
@@ -481,7 +486,10 @@ export default function ChatInterface({
};
const extractedImgs = extractImagesFromContent(aiMessage.content);
- if (extractedImgs.length > 0 && extractedImgs.every((img) => typeof img === "string")) {
+ if (
+ extractedImgs.length > 0 &&
+ extractedImgs.every((img) => typeof img === "string")
+ ) {
aiMessage.webSearchImgs = extractedImgs;
setMessages((prev) => {
const updated = prev.map((m) =>
@@ -2142,27 +2150,41 @@ export default function ChatInterface({
: "border-none mr-6 rounded-md py-2"
)}
>
-
+
+
+
-
+
+
@@ -2578,25 +2600,39 @@ const AppPanelTrigger = () => {
}`}
>
-
+
+
-
+
+
{
-
+
+
);
diff --git a/frontend/components/FileUpload.tsx b/frontend/components/FileUpload.tsx
index ef16ab8..fab5fb0 100644
--- a/frontend/components/FileUpload.tsx
+++ b/frontend/components/FileUpload.tsx
@@ -13,6 +13,7 @@ import { cn } from "@/lib/utils";
import { toast } from "./ui/Toast";
import { devError } from "@/lib/logger";
import { LinkIcon } from "./ui/icons/LinkIcon";
+import { ConditionalTooltip } from "./ui/conditional-tooltip";
interface FileUploadProps {
onFilesUploaded: (attachments: FileAttachment[]) => void;
@@ -231,41 +232,36 @@ export default function FileUpload({
/>
{/* Upload button - Enhanced with better visual feedback */}
-
+
+
{/* Drag and drop overlay - Enhanced responsive design */}
{isDragOver && (
diff --git a/frontend/components/ui/VoiceInputButton.tsx b/frontend/components/ui/VoiceInputButton.tsx
index 8ef802d..ddcfa18 100644
--- a/frontend/components/ui/VoiceInputButton.tsx
+++ b/frontend/components/ui/VoiceInputButton.tsx
@@ -16,6 +16,7 @@ import {
} from "@/lib/audioRecorder";
import { useBYOKStore } from "@/frontend/stores/BYOKStore";
import { cn } from "@/lib/utils";
+import { ConditionalTooltip } from "./conditional-tooltip";
interface VoiceInputButtonProps {
onResult: (text: string) => void;
@@ -458,23 +459,31 @@ export default function VoiceInputButton({
}, [listening]);
return (
-
+
+
);
}
diff --git a/frontend/components/ui/conditional-tooltip.tsx b/frontend/components/ui/conditional-tooltip.tsx
new file mode 100644
index 0000000..abd92f6
--- /dev/null
+++ b/frontend/components/ui/conditional-tooltip.tsx
@@ -0,0 +1,39 @@
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "./tooltip";
+
+interface ConditionalTooltipProps {
+ content: string;
+ showTooltip: boolean;
+ children: React.ReactNode;
+ side: "top" | "right" | "bottom" | "left";
+ className?: string;
+}
+
+export const ConditionalTooltip = ({
+ content,
+ side,
+ showTooltip,
+ children,
+ className,
+}: ConditionalTooltipProps) => {
+ if (showTooltip) {
+ return (
+
+
+ {children}
+
+ {content}
+
+
+
+ );
+ }
+ return <>{children}>;
+};
diff --git a/frontend/components/ui/tooltip.tsx b/frontend/components/ui/tooltip.tsx
index cdeb7a0..487d5d4 100644
--- a/frontend/components/ui/tooltip.tsx
+++ b/frontend/components/ui/tooltip.tsx
@@ -1,17 +1,9 @@
-"use client"
+"use client";
-/**
- * Tooltip Components
- *
- * Used in: frontend/components/ui/sidebar.tsx
- * Purpose: Provides tooltip functionality for hover information.
- * Includes Tooltip, TooltipProvider, TooltipTrigger, TooltipContent components.
- */
+import * as React from "react";
+import * as TooltipPrimitive from "@radix-ui/react-tooltip";
-import * as React from "react"
-import * as TooltipPrimitive from "@radix-ui/react-tooltip"
-
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
function TooltipProvider({
delayDuration = 0,
@@ -23,7 +15,7 @@ function TooltipProvider({
delayDuration={delayDuration}
{...props}
/>
- )
+ );
}
function Tooltip({
@@ -33,13 +25,13 @@ function Tooltip({
- )
+ );
}
function TooltipTrigger({
...props
}: React.ComponentProps) {
- return
+ return ;
}
function TooltipContent({
@@ -54,16 +46,16 @@ function TooltipContent({
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
- "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
+ "bg-gray-800 text-sm text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-balance",
className
)}
{...props}
>
{children}
-
+
- )
+ );
}
-export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
diff --git a/next.config.ts b/next.config.ts
index 5ced8d6..f1c11cf 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -117,5 +117,5 @@ const nextConfig: NextConfig = {
},
};
-// Wrap with Better Stack first, then bundle analyzer
-export default withBundleAnalyzer(withBetterStack(nextConfig));
+// Wrap with bundle analyzer only (Better Stack disabled for dev with dummy credentials)
+export default withBundleAnalyzer(nextConfig);
diff --git a/package.json b/package.json
index 5217c00..0e4e3bc 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "CappyChat",
+ "name": "cappychat",
"version": "4.1.0",
"private": true,
"scripts": {