build ask mode (#444)

This commit is contained in:
Will Chen
2025-06-19 10:42:51 -07:00
committed by GitHub
parent 8464609ba8
commit 9fbd7031d9
28 changed files with 1098 additions and 27 deletions

View File

@@ -1,6 +1,7 @@
import { ContextFilesPicker } from "./ContextFilesPicker";
import { ModelPicker } from "./ModelPicker";
import { ProModeSelector } from "./ProModeSelector";
import { ChatModeSelector } from "./ChatModeSelector";
export function ChatInputControls({
showContextFilesPicker = false,
@@ -9,8 +10,10 @@ export function ChatInputControls({
}) {
return (
<div className="flex">
<ChatModeSelector />
<div className="w-1.5"></div>
<ModelPicker />
<div className="w-2"></div>
<div className="w-1.5"></div>
<ProModeSelector />
<div className="w-1"></div>
{showContextFilesPicker && (

View File

@@ -0,0 +1,70 @@
import {
MiniSelectTrigger,
Select,
SelectContent,
SelectItem,
SelectValue,
} from "@/components/ui/select";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useSettings } from "@/hooks/useSettings";
import type { ChatMode } from "@/lib/schemas";
export function ChatModeSelector() {
const { settings, updateSettings } = useSettings();
const selectedMode = settings?.selectedChatMode || "build";
const handleModeChange = (value: string) => {
updateSettings({ selectedChatMode: value as ChatMode });
};
const getModeDisplayName = (mode: ChatMode) => {
switch (mode) {
case "build":
return "Build";
case "ask":
return "Ask";
default:
return "Build";
}
};
return (
<Select value={selectedMode} onValueChange={handleModeChange}>
<Tooltip>
<TooltipTrigger asChild>
<MiniSelectTrigger
data-testid="chat-mode-selector"
className="h-6 w-fit px-1.5 py-0 text-xs-sm font-medium shadow-none bg-background hover:bg-muted/50 focus:bg-muted/50 gap-0.5"
size="sm"
>
<SelectValue>{getModeDisplayName(selectedMode)}</SelectValue>
</MiniSelectTrigger>
</TooltipTrigger>
<TooltipContent>Open mode menu</TooltipContent>
</Tooltip>
<SelectContent align="start" onCloseAutoFocus={(e) => e.preventDefault()}>
<SelectItem value="build">
<div className="flex flex-col items-start">
<span className="font-medium">Build</span>
<span className="text-xs text-muted-foreground">
Generate and edit code
</span>
</div>
</SelectItem>
<SelectItem value="ask">
<div className="flex flex-col items-start">
<span className="font-medium">Ask</span>
<span className="text-xs text-muted-foreground">
Ask questions about the app
</span>
</div>
</SelectItem>
</SelectContent>
</Select>
);
}

View File

@@ -129,7 +129,7 @@ export function ModelPicker() {
<Button
variant="outline"
size="sm"
className="flex items-center gap-2 h-8 max-w-[160px] px-2"
className="flex items-center gap-2 h-8 max-w-[130px] px-1.5 text-xs-sm"
>
<span className="truncate">
{modelDisplayName === "Auto" && (

View File

@@ -40,10 +40,10 @@ export function ProModeSelector() {
<Button
variant="outline"
size="sm"
className="has-[>svg]:px-2 flex items-center gap-1.5 h-8 border-primary/50 hover:bg-primary/10 font-medium shadow-sm shadow-primary/10 transition-all hover:shadow-md hover:shadow-primary/15"
className="has-[>svg]:px-1.5 flex items-center gap-1.5 h-8 border-primary/50 hover:bg-primary/10 font-medium shadow-sm shadow-primary/10 transition-all hover:shadow-md hover:shadow-primary/15"
>
<Sparkles className="h-4 w-4 text-primary" />
<span className="text-primary font-medium">Pro</span>
<span className="text-primary font-medium text-xs-sm">Pro</span>
</Button>
</PopoverTrigger>
</TooltipTrigger>

View File

@@ -272,23 +272,25 @@ export function ChatInput({ chatId }: { chatId?: number }) {
onDrop={handleDrop}
>
{/* Only render ChatInputActions if proposal is loaded */}
{proposal && proposalResult?.chatId === chatId && (
<ChatInputActions
proposal={proposal}
onApprove={handleApprove}
onReject={handleReject}
isApprovable={
!isProposalLoading &&
!!proposal &&
!!messageId &&
!isApproving &&
!isRejecting &&
!isStreaming
}
isApproving={isApproving}
isRejecting={isRejecting}
/>
)}
{proposal &&
proposalResult?.chatId === chatId &&
settings.selectedChatMode !== "ask" && (
<ChatInputActions
proposal={proposal}
onApprove={handleApprove}
onReject={handleReject}
isApprovable={
!isProposalLoading &&
!!proposal &&
!!messageId &&
!isApproving &&
!isRejecting &&
!isStreaming
}
isApproving={isApproving}
isRejecting={isRejecting}
/>
)}
<SelectedComponentDisplay />

View File

@@ -48,6 +48,31 @@ function SelectTrigger({
);
}
function MiniSelectTrigger({
className,
size = "default",
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default";
}) {
return (
<SelectPrimitive.Trigger
data-slot="select-trigger"
data-size={size}
className={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
>
{children}
{/* <SelectPrimitive.Icon asChild>
<ChevronDownIcon className="size-4 opacity-50" />
</SelectPrimitive.Icon> */}
</SelectPrimitive.Trigger>
);
}
function SelectContent({
className,
children,
@@ -179,5 +204,6 @@ export {
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
MiniSelectTrigger,
SelectValue,
};