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": {