mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): ReasoningContent + compare ModelSelector + CreateProjectModal
- ReasoningContent: antd Button + @ant-design BulbOutlined/Down/Right \u2192 shadcn Button variant=ghost + lucide Lightbulb/Chevron. Code blocks use semantic bg-muted; Markdown renderer unchanged. - compareUI/ModelSelector: antd Select + @tremor TextInput \u2192 shadcn Select + Input. 'Add custom model' option triggers inline input with Enter-to-commit and blur-to-commit. - CreateProjectModal: antd Modal/Typography/Button + @ant-design FolderAddOutlined \u2192 shadcn Dialog + Button + lucide FolderPlus. Inner ProjectBaseForm (antd Form) is preserved. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
parent
baefdd512c
commit
2d876bfb8f
3 changed files with 90 additions and 56 deletions
|
|
@ -1,5 +1,13 @@
|
|||
import { Modal, Form, Button, Typography } from "antd";
|
||||
import { FolderAddOutlined } from "@ant-design/icons";
|
||||
import { FolderPlus } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Form } from "antd";
|
||||
import MessageManager from "@/components/molecules/message_manager";
|
||||
import {
|
||||
useCreateProject,
|
||||
|
|
@ -52,32 +60,30 @@ export function CreateProjectModal({
|
|||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
<Typography.Text strong style={{ fontSize: 18 }}>
|
||||
Create New Project
|
||||
</Typography.Text>
|
||||
}
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onCancel={handleCancel}
|
||||
width={720}
|
||||
destroyOnHidden
|
||||
footer={[
|
||||
<Button key="cancel" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button
|
||||
key="submit"
|
||||
type="primary"
|
||||
icon={<FolderAddOutlined />}
|
||||
loading={createMutation.isPending}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Create Project
|
||||
</Button>,
|
||||
]}
|
||||
onOpenChange={(o) => (!o ? handleCancel() : undefined)}
|
||||
>
|
||||
<ProjectBaseForm form={form} />
|
||||
</Modal>
|
||||
<DialogContent className="max-w-[720px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
Create New Project
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ProjectBaseForm form={form} />
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
disabled={createMutation.isPending}
|
||||
>
|
||||
<FolderPlus className="h-4 w-4" />
|
||||
{createMutation.isPending ? "Creating…" : "Create Project"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import React, { useState } from "react";
|
||||
import { Button } from "antd";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { coy } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import { DownOutlined, RightOutlined, BulbOutlined } from "@ant-design/icons";
|
||||
import { ChevronDown, ChevronRight, Lightbulb } from "lucide-react";
|
||||
|
||||
interface ReasoningContentProps {
|
||||
reasoningContent: string;
|
||||
}
|
||||
|
||||
const ReasoningContent: React.FC<ReasoningContentProps> = ({ reasoningContent }) => {
|
||||
const ReasoningContent: React.FC<ReasoningContentProps> = ({
|
||||
reasoningContent,
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
|
||||
if (!reasoningContent) return null;
|
||||
|
|
@ -17,32 +19,38 @@ const ReasoningContent: React.FC<ReasoningContentProps> = ({ reasoningContent })
|
|||
return (
|
||||
<div className="reasoning-content mt-1 mb-2">
|
||||
<Button
|
||||
type="text"
|
||||
className="flex items-center text-xs text-gray-500 hover:text-gray-700"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="flex items-center text-xs text-muted-foreground hover:text-foreground"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
icon={<BulbOutlined />}
|
||||
>
|
||||
<Lightbulb className="h-3.5 w-3.5" />
|
||||
{isExpanded ? "Hide reasoning" : "Show reasoning"}
|
||||
{isExpanded ? <DownOutlined className="ml-1" /> : <RightOutlined className="ml-1" />}
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-3 w-3 ml-1" />
|
||||
) : (
|
||||
<ChevronRight className="h-3 w-3 ml-1" />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="mt-2 p-3 bg-gray-50 border border-gray-200 rounded-md text-sm text-gray-700">
|
||||
<div className="mt-2 p-3 bg-muted border border-border rounded-md text-sm text-foreground">
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
code({
|
||||
node,
|
||||
inline,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<"code"> & {
|
||||
inline?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
node?: any;
|
||||
}) {
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
return !inline && match ? (
|
||||
<SyntaxHighlighter
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
style={coy as any}
|
||||
language={match[1]}
|
||||
PreTag="div"
|
||||
|
|
@ -52,7 +60,10 @@ const ReasoningContent: React.FC<ReasoningContentProps> = ({ reasoningContent })
|
|||
{String(children).replace(/\n$/, "")}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
<code className={`${className} px-1.5 py-0.5 rounded bg-gray-100 text-sm font-mono`} {...props}>
|
||||
<code
|
||||
className={`${className} px-1.5 py-0.5 rounded bg-muted text-sm font-mono`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
import React, { useMemo, useState } from "react";
|
||||
import { Select } from "antd";
|
||||
import { TextInput } from "@tremor/react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
interface ModelSelectorProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
|
|
@ -8,7 +15,14 @@ interface ModelSelectorProps {
|
|||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
export function ModelSelector({ value, onChange, models, loading, disabled }: ModelSelectorProps) {
|
||||
|
||||
export function ModelSelector({
|
||||
value,
|
||||
onChange,
|
||||
models,
|
||||
loading,
|
||||
disabled,
|
||||
}: ModelSelectorProps) {
|
||||
const [isAddingCustom, setIsAddingCustom] = useState(false);
|
||||
const [customValue, setCustomValue] = useState("");
|
||||
|
||||
|
|
@ -48,31 +62,34 @@ export function ModelSelector({ value, onChange, models, loading, disabled }: Mo
|
|||
setIsAddingCustom(false);
|
||||
setCustomValue("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 min-w-0">
|
||||
<Select<string>
|
||||
<Select
|
||||
value={selectValue}
|
||||
onChange={handleSelectChange}
|
||||
disabled={disabled}
|
||||
loading={loading}
|
||||
placeholder={loading ? "Loading models..." : "Select a model"}
|
||||
className="w-full rounded-md"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
onValueChange={handleSelectChange}
|
||||
disabled={disabled || loading}
|
||||
>
|
||||
{displayOptions.map((model) => (
|
||||
<Select.Option key={model} value={model}>
|
||||
{model}
|
||||
</Select.Option>
|
||||
))}
|
||||
<Select.Option value="__custom__">+ Add custom model</Select.Option>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue
|
||||
placeholder={loading ? "Loading models..." : "Select a model"}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{displayOptions.map((model) => (
|
||||
<SelectItem key={model} value={model}>
|
||||
{model}
|
||||
</SelectItem>
|
||||
))}
|
||||
<SelectItem value="__custom__">+ Add custom model</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{isAddingCustom && (
|
||||
<TextInput
|
||||
<Input
|
||||
className="mt-2"
|
||||
placeholder="Custom Model Name (Enter to add)"
|
||||
value={customValue}
|
||||
onValueChange={setCustomValue}
|
||||
onChange={(e) => setCustomValue(e.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue