Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function AuthForm({

<div className="space-y-4">
{authError && (
<div className="p-3 bg-red-900/20 border border-red-500/30 rounded-lg flex items-center gap-2">
<div className="p-3 bg-red-900/20 border border-red-500/30 rounded flex items-center gap-2">
<AlertCircle className="h-4 w-4 text-red-400" />
<div className="text-red-400 text-sm">{authError}</div>
</div>
Expand Down
22 changes: 20 additions & 2 deletions packages/mcp-cloudflare/src/client/components/chat/chat-input.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { useEffect, useRef } from "react";
import { Send, CircleStop } from "lucide-react";
import { Button } from "../ui/button";

interface ChatInputProps {
input: string;
isLoading: boolean;
isOpen: boolean;
onInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
onStop: () => void;
onSlashCommand?: (command: string) => void;
}

Expand All @@ -15,6 +18,7 @@ export function ChatInput({
isOpen,
onInputChange,
onSubmit,
onStop,
onSlashCommand,
}: ChatInputProps) {
const inputRef = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -59,15 +63,29 @@ export function ChatInput({

return (
<form onSubmit={handleSubmit} className="relative flex-1">
<div className="flex space-x-2 items-center">
<div className="relative">
<input
ref={inputRef}
value={input}
onChange={onInputChange}
placeholder="Ask me anything about your Sentry data..."
disabled={isLoading}
className="flex-1 p-4 bg-slate-800/50 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-violet-300 focus:border-transparent disabled:opacity-50"
className="w-full p-4 pr-12 rounded bg-slate-800/50 text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-violet-300 focus:border-transparent disabled:opacity-50"
/>
<Button
type={isLoading ? "button" : "submit"}
variant="ghost"
onClick={isLoading ? onStop : undefined}
disabled={!isLoading && !input.trim()}
className="absolute right-2 top-1/2 -translate-y-1/2 p-2 text-slate-400 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:text-slate-400 disabled:hover:bg-transparent transition-colors"
title={isLoading ? "Stop generation" : "Send message"}
>
{isLoading ? (
<CircleStop className="h-4 w-4" />
) : (
<Send className="h-4 w-4" />
)}
</Button>
</div>
</form>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const ChatMessages = forwardRef<HTMLDivElement, ChatMessagesProps>(
const errorIsAuth = error ? isAuthError(error) : false;
const errorMessage = error ? getErrorMessage(error) : null;
return (
<div ref={ref} className="flex-1 overflow-y-auto mx-6 mt-6 space-y-4">
<div ref={ref} className="flex-1 mx-6 mt-6 space-y-4">
{/* Empty State when no messages */}
{messages.length === 0 && (
<div className="flex flex-col items-center justify-center h-full">
Expand Down Expand Up @@ -118,7 +118,7 @@ export const ChatMessages = forwardRef<HTMLDivElement, ChatMessagesProps>(

{/* Show error or loading state */}
{error && errorMessage ? (
<div className="mr-8 p-4 bg-red-900/10 border border-red-500/30 rounded-lg">
<div className="mr-8 p-4 bg-red-900/10 border border-red-500/30 rounded">
<div className="flex items-start gap-3">
<AlertCircle className="h-5 w-5 text-red-400 mt-0.5" />
<div className="flex-1">
Expand Down
61 changes: 44 additions & 17 deletions packages/mcp-cloudflare/src/client/components/chat/chat-ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface ChatUIProps {
onClose?: () => void;
onLogout?: () => void;
onSlashCommand?: (command: string) => void;
onSendPrompt?: (prompt: string) => void;
}

export const ChatUI = forwardRef<HTMLDivElement, ChatUIProps>(
Expand All @@ -44,6 +45,7 @@ export const ChatUI = forwardRef<HTMLDivElement, ChatUIProps>(
onClose,
onLogout,
onSlashCommand,
onSendPrompt,
},
ref,
) => {
Expand All @@ -53,23 +55,14 @@ export const ChatUI = forwardRef<HTMLDivElement, ChatUIProps>(
<div className="md:hidden flex items-center justify-between p-4 border-b border-slate-800 flex-shrink-0">
{showControls && (
<>
<Button
type="button"
onClick={onClose}
variant="ghost"
size="icon"
className="cursor-pointer"
title="Close"
>
<Button type="button" onClick={onClose} size="icon" title="Close">
<X className="h-4 w-4" />
</Button>

<Button
type="button"
onClick={onLogout}
variant="ghost"
size="icon"
className="cursor-pointer"
title="Logout"
>
<LogOut className="h-4 w-4" />
Expand All @@ -78,7 +71,7 @@ export const ChatUI = forwardRef<HTMLDivElement, ChatUIProps>(
)}
</div>

<div className="flex-1 flex flex-col min-h-0">
<div className="pb-34 overflow-y-auto">
{/* Chat Messages */}
<ChatMessages
ref={ref}
Expand All @@ -89,18 +82,52 @@ export const ChatUI = forwardRef<HTMLDivElement, ChatUIProps>(
/>

{/* Chat Input - Always pinned at bottom */}
<div
className="flex-shrink-0 p-6"
style={{
paddingBottom: "max(1.5rem, env(safe-area-inset-bottom))",
}}
>
<div className="py-4 px-6 bottom-0 left-0 right-0 absolute bg-slate-950/95 h-34 overflow-hidden">
{/* Sample Prompt Buttons - Always visible above input */}
{onSendPrompt && (
<div className="mb-4 flex flex-wrap gap-2 justify-center">
<Button
type="button"
onClick={() =>
onSendPrompt("What organizations do I have access to?")
}
size="sm"
variant="outline"
>
Get Organizations
</Button>
<Button
type="button"
onClick={() =>
onSendPrompt(
"Show me how to set up the React SDK for error monitoring",
)
}
size="sm"
variant="outline"
>
React SDK Usage
</Button>
<Button
type="button"
onClick={() =>
onSendPrompt("What are my most recent issues?")
}
size="sm"
variant="outline"
>
Recent Issues
</Button>
</div>
)}

<ChatInput
input={input}
isLoading={isChatLoading}
isOpen={isOpen}
onInputChange={onInputChange}
onSubmit={onSubmit}
onStop={onStop || EMPTY_FUNCTION}
onSlashCommand={onSlashCommand}
/>
</div>
Expand Down
29 changes: 14 additions & 15 deletions packages/mcp-cloudflare/src/client/components/chat/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function Chat({ isOpen, onClose, onLogout }: ChatProps) {
reload,
setMessages,
setInput,
append,
} = useChat({
api: "/api/chat",
headers: authToken ? { Authorization: `Bearer ${authToken}` } : undefined,
Expand All @@ -54,7 +55,7 @@ export function Chat({ isOpen, onClose, onLogout }: ChatProps) {
enabled: true,
smooth: true,
dependencies: [messages, status],
delay: status === "streaming" ? 100 : 0, // More frequent updates during streaming
delay: status === "streaming" || status === "submitted" ? 100 : 0, // More frequent updates during loading
});

// Clear messages function - used locally for /clear command and logout
Expand Down Expand Up @@ -123,6 +124,15 @@ export function Chat({ isOpen, onClose, onLogout }: ChatProps) {
}
}, [authToken, error, reload]);

// Handle sending a prompt programmatically
const handleSendPrompt = useCallback(
(prompt: string) => {
// Clear the input and directly send the message using append
append({ role: "user", content: prompt });
},
[append],
);

// Handle slash commands
const handleSlashCommand = (command: string) => {
// Always clear the input first for all commands
Expand Down Expand Up @@ -171,18 +181,6 @@ export function Chat({ isOpen, onClose, onLogout }: ChatProps) {
// Use a single SlidingPanel and transition between auth and chat states
return (
<SlidingPanel isOpen={isOpen} onClose={onClose}>
{/* Mobile close button - always visible */}
<Button
type="button"
onClick={onClose}
variant="default"
className="md:hidden absolute top-4 left-4 z-10 cursor-pointer"
title="Close"
aria-label="Close chat panel"
>
<X className="h-4 w-4" />
</Button>

{/* Auth form with fade transition */}
<div
className={`absolute inset-0 h-full flex flex-col items-center justify-center transition-all duration-500 ease-in-out ${
Expand Down Expand Up @@ -221,16 +219,17 @@ export function Chat({ isOpen, onClose, onLogout }: ChatProps) {
messages={messages}
input={input}
error={error}
isChatLoading={status === "streaming"}
isChatLoading={status === "streaming" || status === "submitted"}
isOpen={isOpen}
showControls={true}
showControls
onInputChange={handleInputChange}
onSubmit={handleSubmit}
onStop={stop}
onRetry={reload}
onClose={onClose}
onLogout={onLogout}
onSlashCommand={handleSlashCommand}
onSendPrompt={handleSendPrompt}
/>
</div>
</SlidingPanel>
Expand Down
2 changes: 2 additions & 0 deletions packages/mcp-cloudflare/src/client/components/chat/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ export interface ChatUIProps {
onRetry?: () => void;
onClose?: () => void;
onLogout?: () => void;
onSlashCommand?: (command: string) => void;
onSendPrompt?: (prompt: string) => void;
}

export interface ChatMessagesProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function AccordionTrigger({
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
className={cn(
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-center justify-between gap-4 rounded-md py-4 text-left font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180 text-lg text-white hover:text-violet-300 cursor-pointer ",
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-center justify-between gap-4 py-4 text-left font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180 text-lg text-white hover:text-violet-300 cursor-pointer ",
className,
)}
{...props}
Expand Down
9 changes: 5 additions & 4 deletions packages/mcp-cloudflare/src/client/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../lib/utils";

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer",
{
variants: {
variant: {
default: "bg-violet-300 text-black shadow-xs hover:bg-violet-300/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20",
outline: "bg-background shadow-xs hover:bg-violet-300 hover:text-black",
outline:
"bg-slate-800/50 border border-slate-600/50 shadow-xs hover:bg-slate-700/50 hover:text-white ",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost: "hover:underline hover:text-violet-300",
"bg-background shadow-xs hover:bg-violet-300 hover:text-black",
ghost: "hover:text-white hover:bg-slate-700/50 ",
link: "text-primary hover:underline hover:text-violet-300 cursor-pointer",
},
size: {
Expand Down
4 changes: 2 additions & 2 deletions packages/mcp-cloudflare/src/client/components/ui/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const Header: React.FC<HeaderProps> = ({
</div>
</div>
<div className="flex items-center gap-2">
<Button variant="outline" asChild>
<Button variant="secondary" asChild>
<a
href="https://github.com/getsentry/sentry-mcp"
target="_blank"
Expand All @@ -36,7 +36,7 @@ export const Header: React.FC<HeaderProps> = ({
</Button>
{isAuthenticated && onLogout && (
<Button
variant="outline"
variant="secondary"
onClick={onLogout}
className="hidden md:flex cursor-pointer"
>
Expand Down
1 change: 0 additions & 1 deletion packages/mcp-test-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
"open": "^10.0.0"
},
"devDependencies": {
"@types/node": "^22.15.32",
"tsdown": "^0.12.8",
"tsx": "^4.20.3",
"typescript": "^5.8.3",
Expand Down