fix chat boxes and remove context indicator

This commit is contained in:
tusharmagar 2026-01-20 01:51:23 +05:30
parent 80fe2dbf86
commit 5cc8b4bb79
3 changed files with 41 additions and 80 deletions

View file

@ -2,11 +2,12 @@ import * as React from 'react'
import { useCallback, useEffect, useState, useRef } from 'react' import { useCallback, useEffect, useState, useRef } from 'react'
import { workspace } from '@x/shared'; import { workspace } from '@x/shared';
import { RunEvent } from '@x/shared/src/runs.js'; import { RunEvent } from '@x/shared/src/runs.js';
import type { ChatStatus, LanguageModelUsage, ToolUIPart } from 'ai'; import type { LanguageModelUsage, ToolUIPart } from 'ai';
import './App.css' import './App.css'
import z from 'zod'; import z from 'zod';
import { Button } from './components/ui/button'; import { Button } from './components/ui/button';
import { CheckIcon, LoaderIcon } from 'lucide-react'; import { CheckIcon, LoaderIcon, ArrowUp } from 'lucide-react';
import { cn } from '@/lib/utils';
import { MarkdownEditor } from './components/markdown-editor'; import { MarkdownEditor } from './components/markdown-editor';
import { ChatInputBar } from './components/chat-button'; import { ChatInputBar } from './components/chat-button';
import { ChatSidebar } from './components/chat-sidebar'; import { ChatSidebar } from './components/chat-sidebar';
@ -27,31 +28,15 @@ import {
MessageResponse, MessageResponse,
} from '@/components/ai-elements/message'; } from '@/components/ai-elements/message';
import { import {
PromptInput,
PromptInputBody,
PromptInputFooter,
type PromptInputMessage, type PromptInputMessage,
PromptInputProvider, PromptInputProvider,
PromptInputSubmit,
PromptInputTextarea, PromptInputTextarea,
PromptInputTools,
usePromptInputController, usePromptInputController,
type FileMention, type FileMention,
} from '@/components/ai-elements/prompt-input'; } from '@/components/ai-elements/prompt-input';
import { Reasoning, ReasoningContent, ReasoningTrigger } from '@/components/ai-elements/reasoning'; import { Reasoning, ReasoningContent, ReasoningTrigger } from '@/components/ai-elements/reasoning';
import { Shimmer } from '@/components/ai-elements/shimmer'; import { Shimmer } from '@/components/ai-elements/shimmer';
import { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput } from '@/components/ai-elements/tool'; import { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput } from '@/components/ai-elements/tool';
import {
Context,
ContextCacheUsage,
ContextContent,
ContextContentBody,
ContextContentHeader,
ContextInputUsage,
ContextOutputUsage,
ContextReasoningUsage,
ContextTrigger,
} from '@/components/ai-elements/context';
import { import {
SidebarInset, SidebarInset,
SidebarProvider, SidebarProvider,
@ -259,61 +244,52 @@ const collectFilePaths = (nodes: TreeNode[]): string[] =>
interface ChatInputInnerProps { interface ChatInputInnerProps {
onSubmit: (message: PromptInputMessage, mentions?: FileMention[]) => void onSubmit: (message: PromptInputMessage, mentions?: FileMention[]) => void
isProcessing: boolean isProcessing: boolean
contextUsage: LanguageModelUsage
maxTokens: number
usedTokens: number
} }
function ChatInputInner({ function ChatInputInner({
onSubmit, onSubmit,
isProcessing, isProcessing,
contextUsage,
maxTokens,
usedTokens,
}: ChatInputInnerProps) { }: ChatInputInnerProps) {
const controller = usePromptInputController() const controller = usePromptInputController()
const message = controller.textInput.value const message = controller.textInput.value
const canSubmit = Boolean(message.trim()) && !isProcessing const canSubmit = Boolean(message.trim()) && !isProcessing
const submitStatus: ChatStatus = isProcessing ? 'streaming' : 'ready'
const handleSubmit = useCallback((msg: PromptInputMessage) => { const handleSubmit = useCallback(() => {
onSubmit(msg, controller.mentions.mentions) if (!canSubmit) return
onSubmit({ text: message.trim(), files: [] }, controller.mentions.mentions)
controller.textInput.clear()
controller.mentions.clearMentions() controller.mentions.clearMentions()
}, [onSubmit, controller.mentions]) }, [canSubmit, message, onSubmit, controller])
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSubmit()
}
}, [handleSubmit])
return ( return (
<PromptInput onSubmit={handleSubmit}> <div className="flex items-center gap-2 bg-background border border-border rounded-3xl shadow-xl px-4 py-2.5">
<PromptInputBody> <PromptInputTextarea
<PromptInputTextarea placeholder="Type your message..."
placeholder="Type your message..." disabled={isProcessing}
disabled={isProcessing} onKeyDown={handleKeyDown}
/> className="min-h-[1.5rem] py-0 border-0 shadow-none focus-visible:ring-0 rounded-none"
</PromptInputBody> />
<PromptInputFooter> <Button
<PromptInputTools> size="icon"
<Context onClick={handleSubmit}
maxTokens={maxTokens} disabled={!canSubmit}
usedTokens={usedTokens} className={cn(
usage={contextUsage} "h-7 w-7 rounded-full shrink-0 transition-all",
> canSubmit
<ContextTrigger size="sm" /> ? "bg-primary text-primary-foreground hover:bg-primary/90"
<ContextContent> : "bg-muted text-muted-foreground"
<ContextContentHeader /> )}
<ContextContentBody> >
<ContextInputUsage /> <ArrowUp className="h-4 w-4" />
<ContextOutputUsage /> </Button>
<ContextReasoningUsage /> </div>
<ContextCacheUsage />
</ContextContentBody>
</ContextContent>
</Context>
</PromptInputTools>
<PromptInputSubmit
disabled={!canSubmit}
status={submitStatus}
/>
</PromptInputFooter>
</PromptInput>
) )
} }
@ -324,9 +300,6 @@ interface ChatInputWithMentionsProps {
visibleFiles: string[] visibleFiles: string[]
onSubmit: (message: PromptInputMessage, mentions?: FileMention[]) => void onSubmit: (message: PromptInputMessage, mentions?: FileMention[]) => void
isProcessing: boolean isProcessing: boolean
contextUsage: LanguageModelUsage
maxTokens: number
usedTokens: number
} }
function ChatInputWithMentions({ function ChatInputWithMentions({
@ -335,18 +308,12 @@ function ChatInputWithMentions({
visibleFiles, visibleFiles,
onSubmit, onSubmit,
isProcessing, isProcessing,
contextUsage,
maxTokens,
usedTokens,
}: ChatInputWithMentionsProps) { }: ChatInputWithMentionsProps) {
return ( return (
<PromptInputProvider knowledgeFiles={knowledgeFiles} recentFiles={recentFiles} visibleFiles={visibleFiles}> <PromptInputProvider knowledgeFiles={knowledgeFiles} recentFiles={recentFiles} visibleFiles={visibleFiles}>
<ChatInputInner <ChatInputInner
onSubmit={onSubmit} onSubmit={onSubmit}
isProcessing={isProcessing} isProcessing={isProcessing}
contextUsage={contextUsage}
maxTokens={maxTokens}
usedTokens={usedTokens}
/> />
</PromptInputProvider> </PromptInputProvider>
) )
@ -1259,7 +1226,7 @@ function App() {
<ConversationScrollButton className="bottom-24" /> <ConversationScrollButton className="bottom-24" />
</Conversation> </Conversation>
<div className="sticky bottom-0 z-10 bg-background pb-4 pt-6 shadow-lg"> <div className="sticky bottom-0 z-10 bg-background pb-12 pt-0 shadow-lg">
<div className="pointer-events-none absolute inset-x-0 -top-6 h-6 bg-linear-to-t from-background to-transparent" /> <div className="pointer-events-none absolute inset-x-0 -top-6 h-6 bg-linear-to-t from-background to-transparent" />
<div className="mx-auto w-full max-w-4xl px-4"> <div className="mx-auto w-full max-w-4xl px-4">
<ChatInputWithMentions <ChatInputWithMentions
@ -1268,9 +1235,6 @@ function App() {
visibleFiles={visibleKnowledgeFiles} visibleFiles={visibleKnowledgeFiles}
onSubmit={handlePromptSubmit} onSubmit={handlePromptSubmit}
isProcessing={isProcessing} isProcessing={isProcessing}
contextUsage={contextUsage}
maxTokens={maxTokens}
usedTokens={usedTokens}
/> />
</div> </div>
</div> </div>

View file

@ -1123,7 +1123,7 @@ export const PromptInputTextarea = ({
}; };
return ( return (
<div ref={containerRef} className="relative flex-1 min-w-0"> <div ref={containerRef} className="relative contents">
{mentionHighlights.hasHighlights && ( {mentionHighlights.hasHighlights && (
<div <div
ref={highlightRef} ref={highlightRef}
@ -1146,10 +1146,7 @@ export const PromptInputTextarea = ({
)} )}
<InputGroupTextarea <InputGroupTextarea
ref={textareaRef} ref={textareaRef}
className={cn( className={cn("field-sizing-content max-h-48 min-h-10", className)}
"field-sizing-content max-h-48 min-h-16 relative z-10",
className
)}
name="message" name="message"
onCompositionEnd={() => setIsComposing(false)} onCompositionEnd={() => setIsComposing(false)}
onCompositionStart={() => setIsComposing(true)} onCompositionStart={() => setIsComposing(true)}

View file

@ -478,7 +478,7 @@ export function ChatSidebar({
{/* Input area - responsive to sidebar width, matches floating bar position exactly */} {/* Input area - responsive to sidebar width, matches floating bar position exactly */}
<div className="absolute bottom-6 left-14 right-6 z-10" ref={containerRef}> <div className="absolute bottom-6 left-14 right-6 z-10" ref={containerRef}>
<div className="flex items-center gap-2 bg-background border border-border rounded-2xl shadow-xl px-4 py-2.5"> <div className="flex items-center gap-2 bg-background border border-border rounded-3xl shadow-xl px-4 py-2.5">
<div className="relative flex-1 min-w-0"> <div className="relative flex-1 min-w-0">
{mentionHighlights.hasHighlights && ( {mentionHighlights.hasHighlights && (
<div <div
@ -518,7 +518,7 @@ export function ChatSidebar({
onClick={handleSubmit} onClick={handleSubmit}
disabled={!canSubmit} disabled={!canSubmit}
className={cn( className={cn(
"h-7 w-7 rounded-full shrink-0 transition-all self-end", "h-7 w-7 rounded-full shrink-0 transition-all",
canSubmit canSubmit
? "bg-primary text-primary-foreground hover:bg-primary/90" ? "bg-primary text-primary-foreground hover:bg-primary/90"
: "bg-muted text-muted-foreground" : "bg-muted text-muted-foreground"