Skip to content

Commit e53c378

Browse files
committed
feat: better UI
1 parent 1b88a4e commit e53c378

File tree

9 files changed

+393
-561
lines changed

9 files changed

+393
-561
lines changed

apps/api/src/agent/prompts/agent.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ Your task is to process the <user_query> according to the current <mode>, while
186186
<case when="mode == 'execute_chat'">
187187
<instructions>
188188
Your goal is to provide a direct, final answer in a single turn.
189-
1. **Think:** In a <thinking_steps> array within your final JSON, briefly outline your plan to construct the query, referencing the specific patterns you will use from the <knowledge_base>.
189+
1. **Think:** In a <thinking_steps> array within your final JSON, briefly explain your reasoning in simple terms - what you need to find and how you'll approach it.
190190
2. **Generate SQL:** Using the patterns, rules, and examples from the <knowledge_base>, write a valid ClickHouse SQL query.
191191
3. **Format Response:** Choose the correct response_type and chart_type. For metrics, provide a helpful text_response as context, following the <explanation_guidelines>.
192192
4. **Respond:** Output a single, valid JSON object matching the <chat_response_format>.
@@ -195,7 +195,7 @@ Your task is to process the <user_query> according to the current <mode>, while
195195
<case when="mode == 'execute_agent_step'">
196196
<instructions>
197197
Your goal is to decide the NEXT SINGLE STEP to solve a complex problem using a tool.
198-
1. **Think:** In a <thinking> array within your JSON, analyze the current state, review any <agent_tool_result>, and determine the most logical next tool to call from <available_tools>.
198+
1. **Think:** In a <thinking> array within your JSON, explain what you need to do next in simple terms to solve the user's question.
199199
2. **Respond:** Output a single, valid JSON object matching the <agent_response_format> to trigger the tool call.
200200
</instructions>
201201
</case>
@@ -622,6 +622,16 @@ Your task is to process the <user_query> according to the current <mode>, while
622622
- **Error rates:** Explain impact and suggest actions. E.g., "Your error rate is 3.2%, affecting 45 users. Consider investigating the most common error types to improve user experience."
623623
- The text_response should add value and interpretation beyond the raw number.
624624
</explanation_guidelines>
625+
<thinking_guidelines>
626+
Keep thinking_steps conversational and focused on the user's goal, not technical implementation:
627+
- ✅ Good: "I need to compare this week's visitors to last week's visitors"
628+
- ✅ Good: "I'll look at unique visitors for both time periods"
629+
- ✅ Good: "This comparison will show if traffic is growing or declining"
630+
- ❌ Avoid: "I will use a CASE statement to categorize visits"
631+
- ❌ Avoid: "The event_name should be 'screen_view' for visitor counts"
632+
- ❌ Avoid: "I will filter for time >= today() - INTERVAL '7' DAY"
633+
Think like you're explaining your approach to a business stakeholder, not a developer.
634+
</thinking_guidelines>
625635
</section>
626636
</knowledge_base>
627637

apps/api/src/agent/utils/stream-utils.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,10 @@ export function createStreamingResponse(updates: StreamingUpdate[]): Response {
3232
});
3333
}
3434

35-
function createThinkingStep(step: string): string {
36-
return `🧠 ${step}`;
37-
}
38-
3935
export function generateThinkingSteps(steps: string[]): StreamingUpdate[] {
4036
const updates: StreamingUpdate[] = [];
4137
for (const step of steps) {
42-
updates.push({ type: 'thinking', content: createThinkingStep(step) });
38+
updates.push({ type: 'thinking', content: step });
4339
}
4440
return updates;
4541
}

apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx

Lines changed: 40 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
ConversationContent,
1818
ConversationScrollButton,
1919
} from '@/components/ai-elements/conversation';
20-
import { Loader } from '@/components/ai-elements/loader';
20+
2121
import {
2222
PromptInput,
2323
PromptInputSubmit,
@@ -26,7 +26,7 @@ import {
2626
} from '@/components/ai-elements/prompt-input';
2727
import { Suggestion, Suggestions } from '@/components/ai-elements/suggestion';
2828
import { Skeleton } from '@/components/ui/skeleton';
29-
import { cn } from '@/lib/utils';
29+
3030
import { modelAtom, websiteDataAtom } from '@/stores/jotai/assistantAtoms';
3131
import { useChat } from '../hooks/use-chat';
3232
import { MessageBubble } from './message-bubble';
@@ -36,38 +36,37 @@ export function ChatSkeleton() {
3636
return (
3737
<div className="flex h-full flex-col overflow-hidden rounded border bg-background">
3838
{/* Header Skeleton */}
39-
<div className="flex flex-shrink-0 items-center justify-between border-b p-2">
40-
<div className="flex items-center gap-2">
41-
<Skeleton className="h-8 w-8 rounded-full" />
39+
<div className="flex flex-shrink-0 items-center justify-between border-b p-4">
40+
<div className="flex items-center gap-3">
41+
<Skeleton className="h-8 w-8 rounded" />
4242
<div>
43-
<Skeleton className="mb-1 h-4 w-24" />
43+
<Skeleton className="mb-2 h-4 w-24" />
4444
<Skeleton className="h-3 w-32" />
4545
</div>
4646
</div>
4747
<Skeleton className="h-8 w-8 rounded" />
4848
</div>
4949
{/* Messages Area Skeleton */}
50-
<div className="flex-1 space-y-2 overflow-y-auto p-2">
51-
<div className="flex animate-pulse gap-2">
52-
<Skeleton className="h-8 w-8 flex-shrink-0 rounded-full" />
50+
<div className="flex-1 space-y-4 overflow-y-auto p-4">
51+
<div className="flex gap-3">
52+
<Skeleton className="h-8 w-8 flex-shrink-0 rounded" />
5353
<Skeleton className="h-12 w-3/4 rounded" />
5454
</div>
55-
<div className="ml-auto flex animate-pulse flex-row-reverse gap-2 delay-75">
56-
<Skeleton className="h-8 w-8 flex-shrink-0 rounded-full" />
55+
<div className="ml-auto flex flex-row-reverse gap-3">
56+
<Skeleton className="h-8 w-8 flex-shrink-0 rounded" />
5757
<Skeleton className="h-10 w-1/2 rounded" />
5858
</div>
59-
<div className="flex animate-pulse gap-2 delay-150">
60-
<Skeleton className="h-8 w-8 flex-shrink-0 rounded-full" />
59+
<div className="flex gap-3">
60+
<Skeleton className="h-8 w-8 flex-shrink-0 rounded" />
6161
<Skeleton className="h-16 w-4/5 rounded" />
6262
</div>
6363
</div>
6464
{/* Input Area Skeleton */}
65-
<div className="flex-shrink-0 border-t p-2">
66-
<div className="flex gap-2">
67-
<Skeleton className="h-9 flex-1 rounded" />
68-
<Skeleton className="h-9 w-9 rounded" />
65+
<div className="flex-shrink-0 border-t p-4">
66+
<div className="flex gap-3">
67+
<Skeleton className="h-10 flex-1 rounded" />
68+
<Skeleton className="h-10 w-10 rounded" />
6969
</div>
70-
<Skeleton className="mt-2 h-3 w-2/3" />
7170
</div>
7271
</div>
7372
);
@@ -90,12 +89,9 @@ export default function ChatSection() {
9089
inputValue,
9190
setInputValue,
9291
isLoading,
93-
isRateLimited,
9492
sendMessage,
9593
scrollToBottom,
9694
resetChat,
97-
handleUpVote,
98-
handleDownVote,
9995
} = useChat();
10096

10197
const hasMessages = messages.length > 1;
@@ -107,7 +103,7 @@ export default function ChatSection() {
107103
}, [isLoading]);
108104

109105
const handleSend = () => {
110-
if (!(isLoading || isRateLimited) && inputValue.trim()) {
106+
if (!isLoading && inputValue.trim()) {
111107
sendMessage(inputValue.trim());
112108
scrollToBottom();
113109
setTimeout(() => inputRef.current?.focus(), 100);
@@ -117,17 +113,10 @@ export default function ChatSection() {
117113
return (
118114
<div className="flex h-full min-h-0 flex-col rounded border bg-background">
119115
{/* Header */}
120-
<div className="flex flex-shrink-0 items-center justify-between border-b p-2">
121-
<div className="flex min-w-0 flex-1 items-center gap-2">
122-
<div className="relative">
123-
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded bg-primary/10">
124-
<BrainIcon className="h-4 w-4 text-primary" weight="duotone" />
125-
</div>
126-
{isLoading && (
127-
<div className="-bottom-0.5 -right-0.5 absolute flex h-3 w-3 items-center justify-center rounded-full bg-green-500">
128-
<Loader className="text-white" size={8} />
129-
</div>
130-
)}
116+
<div className="flex flex-shrink-0 items-center justify-between border-b p-4">
117+
<div className="flex min-w-0 flex-1 items-center gap-3">
118+
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded bg-primary/10">
119+
<BrainIcon className="h-4 w-4 text-primary" weight="duotone" />
131120
</div>
132121
<div className="min-w-0 flex-1">
133122
<h2 className="truncate font-medium text-sm">Databunny</h2>
@@ -146,54 +135,50 @@ export default function ChatSection() {
146135
onClick={resetChat}
147136
tooltip="Reset chat"
148137
>
149-
<ClockCounterClockwiseIcon
150-
className={cn('h-3 w-3', isLoading && 'animate-spin')}
151-
/>
138+
<ClockCounterClockwiseIcon className="h-4 w-4" />
152139
</Action>
153140
</Actions>
154141
</div>
155142

156143
{/* Messages Area */}
157144
<Conversation>
158-
<ConversationContent className="!p-2">
145+
<ConversationContent className="!p-4">
159146
<ConversationScrollButton />
160147
{!(hasMessages || isLoading) && (
161-
<div className="h-full space-y-4">
148+
<div className="h-full space-y-6">
162149
<div className="flex h-full flex-col justify-between">
163-
<div className="space-y-2 py-4 text-center">
164-
<div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded bg-primary/10">
150+
<div className="space-y-4 py-8 text-center">
151+
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded bg-primary/10">
165152
<SparkleIcon
166-
className="h-6 w-6 text-primary"
153+
className="h-4 w-4 text-primary"
167154
weight="duotone"
168155
/>
169156
</div>
170-
<h3 className="font-medium text-base">
171-
Welcome to Databunny
172-
</h3>
173-
<p className="mx-auto max-w-md text-muted-foreground text-sm">
157+
<h3 className="font-medium text-sm">Welcome to Databunny</h3>
158+
<p className="mx-auto max-w-md text-muted-foreground text-xs">
174159
Your data analyst. Ask me about your website analytics,
175160
metrics, and trends.
176161
</p>
177162
</div>
178-
<div className="space-y-2">
163+
<div className="space-y-3">
179164
<div className="flex items-center gap-2 text-muted-foreground text-xs">
180-
<LightningIcon className="h-3 w-3" weight="duotone" />
165+
<LightningIcon className="h-4 w-4" weight="duotone" />
181166
<span>Try these examples:</span>
182167
</div>
183168
<Suggestions>
184169
{quickQuestions.map((question) => (
185170
<Suggestion
186-
disabled={isLoading || isRateLimited}
171+
disabled={isLoading}
187172
key={question.text}
188173
onClick={(suggestion) => {
189-
if (!(isLoading || isRateLimited)) {
174+
if (!isLoading) {
190175
sendMessage(suggestion);
191176
scrollToBottom();
192177
}
193178
}}
194179
suggestion={question.text}
195180
>
196-
<question.icon className="mr-1.5 h-3 w-3 text-primary/70" />
181+
<question.icon className="mr-2 h-4 w-4 text-primary" />
197182
{question.text}
198183
</Suggestion>
199184
))}
@@ -204,7 +189,7 @@ export default function ChatSection() {
204189
)}
205190

206191
{hasMessages && (
207-
<div className="space-y-2">
192+
<div className="space-y-4">
208193
{messages.map((message) => (
209194
<MessageBubble key={message.id} message={message} />
210195
))}
@@ -214,30 +199,26 @@ export default function ChatSection() {
214199
</Conversation>
215200

216201
{/* Input Area */}
217-
<div className="h-min rounded-none">
202+
<div className="border-t p-4">
218203
<PromptInput
219204
onSubmit={(e) => {
220205
e.preventDefault();
221206
handleSend();
222207
}}
223208
>
224209
<PromptInputTextarea
225-
disabled={isLoading || isRateLimited}
210+
disabled={isLoading}
226211
onChange={(e) => setInputValue(e.target.value)}
227212
placeholder={
228-
isLoading
229-
? 'Analyzing...'
230-
: isRateLimited
231-
? 'Rate limited - please wait...'
232-
: 'Ask about your analytics data...'
213+
isLoading ? 'Analyzing...' : 'Ask about your analytics data...'
233214
}
234215
ref={inputRef}
235216
value={inputValue}
236217
/>
237218
<PromptInputToolbar>
238219
<div />
239220
<PromptInputSubmit
240-
disabled={!inputValue.trim() || isLoading || isRateLimited}
221+
disabled={!inputValue.trim() || isLoading}
241222
status={isLoading ? 'submitted' : undefined}
242223
/>
243224
</PromptInputToolbar>

apps/dashboard/app/(main)/websites/[id]/assistant/components/loading-message.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { RobotIcon } from '@phosphor-icons/react';
55
export function LoadingMessage() {
66
return (
77
<div className="flex max-w-[85%] gap-3">
8-
<div className="mt-1 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-muted">
8+
<div className="mt-1 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded bg-muted">
99
<RobotIcon className="h-4 w-4" />
1010
</div>
11-
<div className="mr-2 rounded-lg bg-muted px-4 py-3">
11+
<div className="mr-2 rounded bg-muted px-4 py-3">
1212
<div className="flex items-center gap-2 text-sm">
1313
<div className="flex space-x-1">
14-
<div className="h-2 w-2 animate-bounce rounded-full bg-current [animation-delay:-0.3s]" />
15-
<div className="h-2 w-2 animate-bounce rounded-full bg-current [animation-delay:-0.15s]" />
16-
<div className="h-2 w-2 animate-bounce rounded-full bg-current" />
14+
<div className="h-2 w-2 rounded bg-current" />
15+
<div className="h-2 w-2 rounded bg-current" />
16+
<div className="h-2 w-2 rounded bg-current" />
1717
</div>
1818
<span className="text-muted-foreground">
1919
Databunny is analyzing...

0 commit comments

Comments
 (0)