Skip to content

Commit 2a80b44

Browse files
Update UI components and type definitions
1 parent 18af608 commit 2a80b44

8 files changed

Lines changed: 133 additions & 30 deletions

File tree

components/chat-history.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export function ChatHistory({
123123

124124
return (
125125
<div
126-
className={`group flex items-center gap-2 p-2 rounded-lg cursor-pointer transition-colors hover:bg-accent ${
126+
className={`group flex items-center gap-2 p-2 rounded-lg cursor-pointer transition-colors hover:bg-primary/5 dark:hover:bg-accent ${
127127
isActive ? 'bg-accent border border-border' : ''
128128
}`}
129129
onClick={() => !isEditing && onSessionSelect(session.session_id)}

components/file-tree.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ function FileTreeNode({
148148
return (
149149
<div>
150150
<div
151-
className="flex items-center cursor-pointer hover:bg-muted/50 rounded-sm p-1 group"
151+
className="flex items-center cursor-pointer hover:bg-primary/5 dark:hover:bg-muted/50 rounded-sm p-1 group"
152152
style={{ paddingLeft: level * 16 + 4 }}
153153
onClick={handleToggle}
154154
>

components/fragment-code.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export function FragmentCode({
7171
{validFiles.map((file) => (
7272
<div
7373
key={file.name}
74-
className={`flex gap-2 select-none cursor-pointer items-center text-sm text-muted-foreground px-2 py-1 rounded-md hover:bg-muted border ${
74+
className={`flex gap-2 select-none cursor-pointer items-center text-sm text-muted-foreground px-2 py-1 rounded-md hover:bg-primary/5 dark:hover:bg-muted border ${
7575
file.name === currentFile ? 'bg-muted border-muted' : ''
7676
}`}
7777
onClick={() => setCurrentFile(file.name)}

components/sidebar.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -187,22 +187,22 @@ export const Sidebar: React.FC<SidebarProps> = ({
187187
variant="ghost"
188188
size="icon"
189189
onClick={handleOpenSidebar}
190-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
190+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
191191
aria-label="Open sidebar"
192192
>
193193
<Menu className="h-5 w-5" />
194194
</Button>
195-
195+
196196
<Button
197197
variant="ghost"
198198
size="icon"
199199
onClick={onStartNewChat}
200-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
200+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
201201
aria-label="Start new chat"
202202
>
203203
<Plus className="h-5 w-5" />
204204
</Button>
205-
205+
206206
<Button
207207
variant="ghost"
208208
size="icon"
@@ -216,7 +216,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
216216
}
217217
}, 100)
218218
}}
219-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
219+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
220220
aria-label="Search"
221221
>
222222
<Search className="h-5 w-5" />
@@ -226,7 +226,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
226226
variant="ghost"
227227
size="icon"
228228
asChild
229-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
229+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
230230
aria-label="Tasks"
231231
>
232232
<Link href="/tasks">
@@ -253,7 +253,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
253253
variant="ghost"
254254
size="icon"
255255
asChild
256-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
256+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
257257
aria-label="Settings"
258258
>
259259
<Link href="/settings">
@@ -264,7 +264,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
264264
<Button
265265
variant="ghost"
266266
size="icon"
267-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
267+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
268268
aria-label="Help Center"
269269
>
270270
<HelpCircle className="h-5 w-5" />
@@ -274,7 +274,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
274274
variant="ghost"
275275
size="icon"
276276
onClick={() => setIsPricingModalOpen(true)}
277-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
277+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
278278
aria-label="My Subscription"
279279
>
280280
<CreditCard className="h-5 w-5" />
@@ -283,7 +283,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
283283
variant="ghost"
284284
size="icon"
285285
onClick={onSignOut}
286-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
286+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
287287
aria-label="Sign Out"
288288
>
289289
<LogOut className="h-5 w-5" />
@@ -292,7 +292,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
292292
</div>
293293

294294
{/* Collapsible Sidebar Content */}
295-
<div
295+
<div
296296
className={`h-screen bg-background border-r border-border flex flex-col transition-all duration-300 ease-in-out ${
297297
isOpen ? 'w-80 opacity-100 translate-x-0' : 'w-0 opacity-0 -translate-x-full overflow-hidden'
298298
}`}
@@ -308,7 +308,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
308308
variant="ghost"
309309
size="icon"
310310
onClick={handleCloseSidebar}
311-
className="h-8 w-8 text-muted-foreground hover:text-foreground transition-colors"
311+
className="h-8 w-8 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
312312
aria-label="Close sidebar"
313313
>
314314
<X className="h-4 w-4" />
@@ -353,7 +353,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
353353
<DropdownMenuTrigger asChild>
354354
<Button
355355
variant="ghost"
356-
className="w-full justify-start gap-2 text-muted-foreground hover:text-foreground hover:bg-muted/50 group transition-colors"
356+
className="w-full justify-start gap-2 text-muted-foreground hover:text-primary dark:hover:text-foreground hover:bg-primary/5 dark:hover:bg-muted/50 group transition-colors"
357357
>
358358
<MessageCircle className="h-4 w-4 flex-shrink-0" />
359359
<span className="truncate">{chat.title}</span>
@@ -395,7 +395,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
395395
<Button
396396
variant="ghost"
397397
asChild
398-
className="w-full justify-start gap-3 text-muted-foreground hover:text-foreground transition-colors"
398+
className="w-full justify-start gap-3 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
399399
>
400400
<Link href="/settings">
401401
<Settings className="h-4 w-4" />
@@ -406,7 +406,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
406406
<HelpModal trigger={
407407
<Button
408408
variant="ghost"
409-
className="w-full justify-start gap-3 text-muted-foreground hover:text-foreground transition-colors"
409+
className="w-full justify-start gap-3 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
410410
>
411411
<HelpCircle className="h-4 w-4" />
412412
Help Center
@@ -416,7 +416,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
416416
<Button
417417
variant="ghost"
418418
onClick={() => setIsPricingModalOpen(true)}
419-
className="w-full justify-start gap-3 text-muted-foreground hover:text-foreground transition-colors"
419+
className="w-full justify-start gap-3 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
420420
>
421421
<CreditCard className="h-4 w-4" />
422422
My Subscription
@@ -425,7 +425,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
425425
<Button
426426
variant="ghost"
427427
onClick={onSignOut}
428-
className="w-full justify-start gap-3 text-muted-foreground hover:text-foreground transition-colors"
428+
className="w-full justify-start gap-3 text-muted-foreground hover:text-primary dark:hover:text-foreground transition-colors"
429429
>
430430
<LogOut className="h-4 w-4" />
431431
Sign Out

components/ui/ai-prompt-box.tsx

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { ChatSettings } from '../chat-settings'
88
import { ChatPicker } from '../chat-picker'
99
import { LLMModel, LLMModelConfig } from '@/lib/models'
1010
import { TemplateId, Templates } from '@/lib/templates'
11+
import { getMatchingCommands, isSlashCommand, extractCommand, SlashCommand } from '@/lib/slash-commands'
12+
import { SlashCommandMenu } from '../slash-command-menu'
1113

1214
const cn = (...classes: (string | undefined | null | false)[]) => classes.filter(Boolean).join(" ");
1315

@@ -101,7 +103,7 @@ const DialogContent = React.forwardRef<
101103
{...props}
102104
>
103105
{children}
104-
<DialogPrimitive.Close className="absolute right-4 top-4 z-10 rounded-full bg-muted/80 p-2 hover:bg-muted transition-all">
106+
<DialogPrimitive.Close className="absolute right-4 top-4 z-10 rounded-full bg-muted/80 p-2 hover:bg-primary/10 dark:hover:bg-muted transition-all">
105107
<X className="h-5 w-5 text-muted-foreground hover:text-primary" />
106108
<span className="sr-only">Close</span>
107109
</DialogPrimitive.Close>
@@ -131,8 +133,8 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
131133
({ className, variant = "default", size = "default", ...props }, ref) => {
132134
const variantClasses = {
133135
default: "bg-primary hover:bg-primary/80 text-primary-foreground",
134-
outline: "border bg-transparent hover:bg-muted",
135-
ghost: "bg-transparent hover:bg-muted",
136+
outline: "border bg-transparent hover:bg-primary/5 dark:hover:bg-muted",
137+
ghost: "bg-transparent hover:bg-primary/5 dark:hover:bg-muted",
136138
};
137139
const sizeClasses = {
138140
default: "h-10 px-4 py-2",
@@ -351,12 +353,24 @@ PromptInput.displayName = "PromptInput";
351353
interface PromptInputTextareaProps {
352354
disableAutosize?: boolean;
353355
placeholder?: string;
356+
showSlashCommands?: boolean;
357+
matchingCommandsCount?: number;
358+
selectedCommandIndex?: number;
359+
onCommandNavigate?: (direction: 'up' | 'down') => void;
360+
onCommandSelect?: () => void;
361+
onCommandCancel?: () => void;
354362
}
355363
const PromptInputTextarea: React.FC<PromptInputTextareaProps & React.ComponentProps<typeof Textarea>> = ({
356364
className,
357365
onKeyDown,
358366
disableAutosize = false,
359367
placeholder,
368+
showSlashCommands = false,
369+
matchingCommandsCount = 0,
370+
selectedCommandIndex = 0,
371+
onCommandNavigate,
372+
onCommandSelect,
373+
onCommandCancel,
360374
...props
361375
}) => {
362376
const { value, setValue, maxHeight, onSubmit, disabled } = usePromptInput();
@@ -372,6 +386,29 @@ const PromptInputTextarea: React.FC<PromptInputTextareaProps & React.ComponentPr
372386
}, [value, maxHeight, disableAutosize]);
373387

374388
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
389+
if (showSlashCommands) {
390+
if (e.key === 'ArrowDown') {
391+
e.preventDefault();
392+
onCommandNavigate?.('down');
393+
return;
394+
}
395+
if (e.key === 'ArrowUp') {
396+
e.preventDefault();
397+
onCommandNavigate?.('up');
398+
return;
399+
}
400+
if (e.key === 'Enter' && !e.shiftKey) {
401+
e.preventDefault();
402+
onCommandSelect?.();
403+
return;
404+
}
405+
if (e.key === 'Escape') {
406+
e.preventDefault();
407+
onCommandCancel?.();
408+
return;
409+
}
410+
}
411+
375412
if (e.key === "Enter" && !e.shiftKey) {
376413
e.preventDefault();
377414
onSubmit?.();
@@ -475,6 +512,9 @@ export const PromptInputBox = React.forwardRef((props: PromptInputBoxProps, ref:
475512
const [showCanvas, setShowCanvas] = React.useState(false);
476513
const uploadInputRef = React.useRef<HTMLInputElement>(null);
477514
const promptBoxRef = React.useRef<HTMLDivElement>(null);
515+
const [showSlashCommands, setShowSlashCommands] = React.useState(false);
516+
const [matchingCommands, setMatchingCommands] = React.useState<SlashCommand[]>([]);
517+
const [selectedCommandIndex, setSelectedCommandIndex] = React.useState(0);
478518

479519
const handleToggleChange = (value: string) => {
480520
if (value === "search") {
@@ -562,9 +602,56 @@ export const PromptInputBox = React.forwardRef((props: PromptInputBoxProps, ref:
562602
setInput("");
563603
setFiles([]);
564604
setFilePreviews({});
605+
setShowSlashCommands(false);
606+
setMatchingCommands([]);
607+
setSelectedCommandIndex(0);
608+
}
609+
};
610+
611+
const handleCommandSelect = (command: SlashCommand) => {
612+
setInput(command.command + ' ');
613+
setShowSlashCommands(false);
614+
setMatchingCommands([]);
615+
setSelectedCommandIndex(0);
616+
};
617+
618+
const handleCommandNavigate = (direction: 'up' | 'down') => {
619+
if (matchingCommands.length === 0) return;
620+
621+
setSelectedCommandIndex(prevIndex => {
622+
if (direction === 'down') {
623+
return (prevIndex + 1) % matchingCommands.length;
624+
} else {
625+
return prevIndex === 0 ? matchingCommands.length - 1 : prevIndex - 1;
626+
}
627+
});
628+
};
629+
630+
const handleSelectCurrentCommand = () => {
631+
if (matchingCommands.length > 0) {
632+
handleCommandSelect(matchingCommands[selectedCommandIndex]);
565633
}
566634
};
567635

636+
const handleCancelCommands = () => {
637+
setShowSlashCommands(false);
638+
setMatchingCommands([]);
639+
setSelectedCommandIndex(0);
640+
};
641+
642+
React.useEffect(() => {
643+
if (isSlashCommand(input)) {
644+
const commands = getMatchingCommands(input);
645+
setMatchingCommands(commands);
646+
setShowSlashCommands(commands.length > 0);
647+
setSelectedCommandIndex(0);
648+
} else {
649+
setShowSlashCommands(false);
650+
setMatchingCommands([]);
651+
setSelectedCommandIndex(0);
652+
}
653+
}, [input]);
654+
568655
const handleStartRecording = () => console.log("Started recording");
569656

570657
const handleStopRecording = (duration: number) => {
@@ -645,7 +732,7 @@ export const PromptInputBox = React.forwardRef((props: PromptInputBoxProps, ref:
645732

646733
<div
647734
className={cn(
648-
"transition-all duration-300",
735+
"transition-all duration-300 relative",
649736
isRecording ? "h-0 overflow-hidden opacity-0" : "opacity-100"
650737
)}
651738
>
@@ -660,7 +747,21 @@ export const PromptInputBox = React.forwardRef((props: PromptInputBoxProps, ref:
660747
: placeholder
661748
}
662749
className="text-base"
750+
showSlashCommands={showSlashCommands}
751+
matchingCommandsCount={matchingCommands.length}
752+
selectedCommandIndex={selectedCommandIndex}
753+
onCommandNavigate={handleCommandNavigate}
754+
onCommandSelect={handleSelectCurrentCommand}
755+
onCommandCancel={handleCancelCommands}
663756
/>
757+
{showSlashCommands && (
758+
<SlashCommandMenu
759+
commands={matchingCommands}
760+
selectedIndex={selectedCommandIndex}
761+
onSelect={handleCommandSelect}
762+
position={{ top: 0, left: 0 }}
763+
/>
764+
)}
664765
</div>
665766

666767
{isRecording && (
@@ -681,7 +782,7 @@ export const PromptInputBox = React.forwardRef((props: PromptInputBoxProps, ref:
681782
<PromptInputAction tooltip="Upload image">
682783
<button
683784
onClick={() => uploadInputRef.current?.click()}
684-
className="flex h-8 w-8 text-muted-foreground cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-muted/30 hover:text-primary"
785+
className="flex h-8 w-8 text-muted-foreground cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-primary/5 dark:hover:bg-muted/30 hover:text-primary"
685786
disabled={isRecording}
686787
>
687788
<Paperclip className="h-5 w-5 transition-colors" />
@@ -824,10 +925,10 @@ export const PromptInputBox = React.forwardRef((props: PromptInputBoxProps, ref:
824925
className={cn(
825926
"h-8 w-8 rounded-full transition-all duration-200",
826927
isRecording
827-
? "bg-transparent hover:bg-muted/30 text-red-500 hover:text-red-400"
928+
? "bg-transparent hover:bg-red-50 dark:hover:bg-muted/30 text-red-500 hover:text-red-400"
828929
: hasContent
829930
? "bg-primary hover:bg-primary/80 text-primary-foreground"
830-
: "bg-transparent hover:bg-muted/30 text-muted-foreground hover:text-primary"
931+
: "bg-transparent hover:bg-primary/5 dark:hover:bg-muted/30 text-muted-foreground hover:text-primary"
831932
)}
832933
onClick={() => {
833934
if (isRecording) setIsRecording(false);

components/ui/alert-dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const AlertDialogCancel = React.forwardRef<
120120
<AlertDialogPrimitive.Cancel
121121
ref={ref}
122122
className={cn(
123-
"mt-2 inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 sm:mt-0",
123+
"mt-2 inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium ring-offset-background transition-colors hover:bg-primary/5 dark:hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 sm:mt-0",
124124
className
125125
)}
126126
{...props}

components/ui/button.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ const buttonVariants = cva(
1313
destructive:
1414
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
1515
outline:
16-
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
16+
'border border-input bg-background shadow-sm hover:bg-primary/5 dark:hover:bg-accent hover:text-accent-foreground',
1717
secondary:
1818
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
19-
ghost: 'hover:bg-accent hover:text-accent-foreground',
19+
ghost: 'hover:bg-primary/5 dark:hover:bg-accent hover:text-accent-foreground',
2020
link: 'text-primary underline-offset-4 hover:underline',
2121
},
2222
size: {

0 commit comments

Comments
 (0)