Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 93 additions & 65 deletions frontend/doc.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion frontend/src/components/console/nav/nav-balance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export default function NavBalance({ variant = "sidebar" }: NavBalanceProps) {
const triggerContent = (
<>
<IconWallet className={variant === "header" ? "h-[1.2rem] w-[1.2rem]" : "size-5"} />
余额: {Math.ceil(balance + bonus).toLocaleString()} 点
钱包
</>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Spinner } from "@/components/ui/spinner"
import { selectHost, selectImage, selectModel } from "@/utils/common"
Expand All @@ -29,6 +30,7 @@ export default function StartDevelopTaskDialog({
const [branches, setBranches] = useState<string[]>([])
const [selectedBranch, setSelectedBranch] = useState<string>('')
const [loadingBranches, setLoadingBranches] = useState<boolean>(false)
const [userMessage, setUserMessage] = useState<string>('')
const { images, models, hosts } = useCommonData()

const fetchBranches = async () => {
Expand Down Expand Up @@ -86,6 +88,7 @@ export default function StartDevelopTaskDialog({
useEffect(() => {
if (open) {
fetchBranches()
setUserMessage('')
}
}, [open, project])

Expand All @@ -99,7 +102,7 @@ export default function StartDevelopTaskDialog({

// 创建任务
await apiRequest('v1UsersTasksCreate', {
content: `你好,MonkeyCode`,
content: userMessage.trim(),
cli_name: ConstsCliName.CliNameOpencode,
model_id: selectModel(models, false),
image_id: selectImage(images, false),
Expand Down Expand Up @@ -177,6 +180,16 @@ export default function StartDevelopTaskDialog({
</Select>
</div>
)}
<div className="space-y-2">
<Label>任务内容</Label>
<Textarea
value={userMessage}
onChange={(e) => setUserMessage(e.target.value)}
placeholder="请输入任务内容"
rows={4}
className="resize-none"
/>
</div>
</div>

<DialogFooter>
Expand Down
60 changes: 31 additions & 29 deletions frontend/src/components/console/task/message-toolcall.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { IconAlertTriangle, IconCircleCheck } from "@tabler/icons-react"
import { IconAlertTriangle, IconChevronDown, IconChevronUp, IconCircleCheck } from "@tabler/icons-react"
import { Spinner } from "@/components/ui/spinner"
import type { MessageType } from "./message"
import { Badge } from "@/components/ui/badge"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { useMemo } from "react"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
import { useMemo, useState } from "react"
import { ConstsCliName } from "@/api/Api"
import * as fallbackRender from "./toolcalls/fallback"
import * as opencodeSearchRender from "./toolcalls/opencode_search"
import * as opencodeReadRender from "./toolcalls/opencode_read"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import * as opencodeEditRender from "./toolcalls/opencode_edit"
import * as claudeEditRender from "./toolcalls/claude_edit"
import * as claudeReadRender from "./toolcalls/claude_read"
Expand Down Expand Up @@ -63,32 +61,36 @@ export const ToolCallMessageItem = ({ message, cli }: { message: MessageType, cl
return renderDetail(message)
}, [message])

const [open, setOpen] = useState(false)

return (
<Dialog>
<DialogTrigger asChild>
<Badge variant="outline" className="max-w-[80%] cursor-pointer hover:text-primary">
{renderStatus()}
<div className="min-w-0 flex-1 whitespace-normal line-clamp-1 break-all">
{title}
</div>
</Badge>
</DialogTrigger>
<DialogContent className="w-fit min-w-[480px] sm:max-w-[90vw] md:max-w-[80vw] lg:max-w-[70vw] xl:max-w-[60vw]">
<DialogHeader>
<Tooltip>
<TooltipTrigger asChild>
<DialogTitle className="line-clamp-1 break-all w-fit">
{title}
</DialogTitle>
</TooltipTrigger>
<TooltipContent side="bottom">
<Collapsible open={open} onOpenChange={setOpen} className="w-full max-w-[80%]">
<div className="flex items-center gap-1.5 rounded-md border border-border px-3 py-2 hover:bg-muted/30 transition-colors">
<CollapsibleTrigger asChild>
<button
type="button"
className="flex items-center gap-1.5 min-w-0 flex-1 text-left cursor-pointer outline-none"
>
{renderStatus()}
<span className="min-w-0 flex-1 whitespace-normal line-clamp-1 break-all text-xs">
{title}
</TooltipContent>
</Tooltip>
</DialogHeader>
{detail}
</DialogContent>
</Dialog>
</span>
<span className="shrink-0 text-muted-foreground">
{open ? (
<IconChevronUp className="size-4" />
) : (
<IconChevronDown className="size-4" />
)}
</span>
</button>
</CollapsibleTrigger>
</div>
<CollapsibleContent>
<div className="mt-1 rounded-md border border-border bg-muted/30 text-xs max-h-[50vh] overflow-auto">
{detail}
</div>
</CollapsibleContent>
</Collapsible>
)
}

Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function TaskPreparingView({ task }: TaskPreparingProps) {
const detailMessage = task?.virtualmachine?.conditions?.[task?.virtualmachine?.conditions?.length - 1]?.message || "正在准备开发环境..."

return (
<Empty className="flex-1 border border-dashed">
<Empty className="flex-1 bg-muted/60">
<EmptyHeader className="md:max-w-2xl">
<EmptyMedia variant="icon">
<TaskPreparingIcon task={task} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const renderDetail = (message: MessageType) => {

return (
<div
className="text-xs max-h-[50vh] overflow-auto border rounded-md"
className="text-xs p-3"
style={{ '--diff-font-family': 'var(--font-google-sans-code)' } as React.CSSProperties}
>
{files.map((file, index) => (
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/components/console/task/toolcalls/claude_read.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { MessageType } from "../message";
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty";
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia } from "@/components/ui/empty";
import { IconFileText } from "@tabler/icons-react";

export const renderTitle = (message: MessageType) => {
Expand All @@ -16,17 +16,17 @@ export const renderDetail = (message: MessageType) => {
})

if ((lines || []).length === 0) {
return <Empty className="border">
return <Empty className="">
<EmptyHeader>
<EmptyMedia variant="icon">
<IconFileText className="" />
<IconFileText className="size-6 opacity-50" />
</EmptyMedia>
<EmptyTitle>没有内容</EmptyTitle>
<EmptyDescription>没有内容</EmptyDescription>
</EmptyHeader>
</Empty>
}

return <div className="text-xs flex flex-col max-h-[50vh] overflow-auto bg-accent/30 rounded-md">
return <div className="text-xs flex flex-col p-3">
<div className="w-12 pl-2 bg-accent min-h-2"></div>
{lines.map((line: any) => {
return (
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/console/task/toolcalls/fallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const renderTitle = (message: MessageType) => {


if (input.length > 0) {
return <div className="flex flex-col gap-2 overflow-auto max-h-[50vh] bg-accent/50 rounded-md p-2 text-xs">
return <div className="flex flex-col gap-2 text-xs p-3">
<pre className="">
<code className="text-primary">{cwd}</code>
<code className="text-muted-foreground">$ </code>
Expand All @@ -89,7 +89,7 @@ export const renderTitle = (message: MessageType) => {
}

return <>
<pre className="text-xs overflow-auto p-2 bg-accent/50 rounded-md max-h-[50vh]">
<pre className="text-xs">
{JSON.stringify(message.data, null, 2)}
</pre>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const renderDetail = (message: MessageType) => {

return (
<div
className="text-xs max-h-[50vh] overflow-auto border rounded-md"
className="text-xs"
style={{ '--diff-font-family': 'var(--font-google-sans-code)' } as React.CSSProperties}
>
<style>{`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const renderTitle = (message: MessageType) => {

export const renderDetail = (message: MessageType) => {
return <>
<pre className="text-xs overflow-auto p-2 bg-accent/50 rounded-md max-h-[50vh]">
<pre className="text-xs p-3">
{message.data.rawOutput?.output || message.data.rawOutput?.error}
</pre>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const renderTitle = (message: MessageType) => {

export const renderDetail = (message: MessageType) => {
return <>
<pre className="text-xs overflow-auto p-2 bg-accent/50 rounded-md max-h-[50vh]">
<pre className="text-xs p-3">
{message.data.rawOutput?.output}
</pre>
</>
Expand Down
34 changes: 8 additions & 26 deletions frontend/src/components/console/task/toolcalls/opencode_read.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { MessageType } from "../message";
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty";
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia } from "@/components/ui/empty";
import { IconFileText } from "@tabler/icons-react";


Expand All @@ -8,37 +8,19 @@ export const renderTitle = (message: MessageType) => {
}

export const renderDetail = (message: MessageType) => {

const lines = message.data.rawOutput?.output?.split('\n').map((line: string) => {
return line.match(/^(\d+)\| (.*)$/)
}).filter((line: any) => line !== null).map((line: any) => {
return {
number: parseInt(line[1]),
content: line[2]
}
})

if ((lines || []).length === 0) {
return <Empty className="border">
if ((message.data.rawOutput?.output || '').trim().length === 0) {
return <Empty className="">
<EmptyHeader>
<EmptyMedia variant="icon">
<IconFileText className="" />
<IconFileText className="size-6 opacity-50" />
</EmptyMedia>
<EmptyTitle>没有内容</EmptyTitle>
<EmptyDescription>没有内容</EmptyDescription>
</EmptyHeader>
</Empty>
}

return <div className="text-xs flex flex-col max-h-[50vh] overflow-auto bg-accent/30 rounded-md">
<div className="w-12 pl-2 bg-accent min-h-2"></div>
{lines.map((line: any) => {
return (
<div key={line.number} className="flex flex-row h-4.5">
<div className="text-muted-foreground w-12 select-none pl-2 flex items-center flex-shrink-0 bg-accent">{line.number}</div>
<div className="whitespace-pre flex-1 pr-2 flex items-center px-2">{line.content}</div>
</div>
)
})}
<div className="w-12 pl-2 bg-accent min-h-2"></div>
</div>
return <pre className="text-xs p-3">
{message.data.rawOutput?.output}
</pre>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const renderTitle = (message: MessageType) => {
}

export const renderDetail = (message: MessageType) => {
return <pre className="text-xs p-2 bg-accent/50 rounded-md max-h-[50vh] overflow-auto">
return <pre className="text-xs p-3">
{message.data.rawOutput?.output}
</pre>
}
10 changes: 5 additions & 5 deletions frontend/src/components/welcome/banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { Button } from "@/components/ui/button";

const Banner = () => {
return (
<div className="w-full px-10 mt-60">
<div className="w-full max-w-[1200px] mx-auto flex flex-col gap-6">
<div className="w-full px-6 sm:px-10 mt-48 sm:mt-56">
<div className="w-full max-w-[1200px] mx-auto flex flex-col gap-4">
<h1 className="text-balance text-5xl font-bold tracking-tight leading-tight">
MonkeyCode 智能开发平台
MonkeyCode — 免费的 AI 编程平台
</h1>
<p className="text-pretty text-base text-muted-foreground sm:text-lg">
MonkeyCode 不是 AI 编程工具,是对传统研发模式的变革,是全新的 AI 编程体验,让你的研发团队效率 Max
<p className="text-base text-muted-foreground sm:text-lg leading-relaxed">
说需求,AI 写代码、做设计、做 Review。云开发环境开箱即用,多模型不限量
</p>
<div className="flex flex-row gap-4">
<Button size="lg" asChild><a href="/console/">开始使用</a></Button>
Expand Down
56 changes: 56 additions & 0 deletions frontend/src/components/welcome/highlights.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
IconGift,
IconCloud,
IconCpu,
IconBrandOpenai,
} from "@tabler/icons-react";

const Highlights = () => {
const items = [
{
icon: IconGift,
title: "完全免费",
description: "零成本使用,无需订阅费用,节省开发环境和大模型费用。",
},
{
icon: IconCloud,
title: "云开发环境",
description: "内置云开发环境,开箱即用,无需本地配置。",
},
{
icon: IconCpu,
title: "2 核 8GB 云服务器",
description: "每个开发任务对应一台独立云服务器,环境隔离、安全可控。",
},
{
icon: IconBrandOpenai,
title: "多模型不限量",
description: "内置 GLM、MiniMax、Kimi、Deepseek 等大模型,不限额度,无限畅用。",
},
];

return (
<div className="w-full px-6 sm:px-10 py-10 sm:py-12">
<div className="w-full max-w-[1200px] mx-auto">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{items.map((item, index) => (
<div
key={index}
className="flex flex-col gap-2 p-4 rounded-xl border bg-card/50 hover:bg-card hover:shadow-sm transition-all"
>
<div className="w-9 h-9 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
<item.icon className="size-4 text-primary" />
</div>
<h3 className="font-semibold text-base">{item.title}</h3>
<p className="text-xs text-muted-foreground leading-relaxed">
{item.description}
</p>
</div>
))}
</div>
</div>
</div>
);
};

export default Highlights;
22 changes: 11 additions & 11 deletions frontend/src/components/welcome/numbers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ const Numbers = () => {
};

return (
<div className="w-full px-10 py-24">
<div className="border rounded-md flex flex-col md:flex-row gap-10 md:gap-0 justify-between max-w-[1200px] w-full mx-auto py-6 px-16">
<div className="text-center flex flex-col items-center gap-2">
<div className="text-muted-foreground">注册用户</div>
<div className="text-4xl cursor-default hover:font-semibold">{ntos(stats.users_count || 0)}</div>
<div className="w-full px-6 sm:px-10 py-12 sm:py-16">
<div className="max-w-[1200px] w-full mx-auto flex flex-col sm:flex-row gap-8 sm:gap-0 sm:divide-x divide-border">
<div className="flex-1 flex flex-col items-center justify-center gap-1 py-4">
<div className="text-sm text-muted-foreground">注册用户</div>
<div className="text-3xl sm:text-4xl font-bold tabular-nums">{ntos(stats.users_count || 0)}</div>
</div>
<div className="text-center flex flex-col items-center gap-2">
<div className="text-muted-foreground">完成开发任务</div>
<div className="text-4xl cursor-default hover:font-semibold">{ntos(stats.tasks_count || 0)}</div>
<div className="flex-1 flex flex-col items-center justify-center gap-1 py-4">
<div className="text-sm text-muted-foreground">完成开发任务</div>
<div className="text-3xl sm:text-4xl font-bold tabular-nums">{ntos(stats.tasks_count || 0)}</div>
</div>
<div className="text-center flex flex-col items-center gap-2">
<div className="text-muted-foreground">GitHub Star</div>
<div className="text-4xl cursor-default hover:font-semibold cursor-pointer" onClick={() => window.open("https://github.com/chaitin/monkeycode", "_blank")}>{ntos(stats.repo_stars || 0)}</div>
<div className="flex-1 flex flex-col items-center justify-center gap-1 py-4">
<div className="text-sm text-muted-foreground">GitHub Star</div>
<div className="text-3xl sm:text-4xl font-bold tabular-nums cursor-pointer hover:text-primary transition-colors" onClick={() => window.open("https://github.com/chaitin/monkeycode", "_blank")}>{ntos(stats.repo_stars || 0)}</div>
</div>
</div>
</div>
Expand Down
Loading
Loading