diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 24d7cc6..a5c70ae 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,7 @@ { - "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] + "recommendations": [ + "tauri-apps.tauri-vscode", + "rust-lang.rust-analyzer", + "github.copilot-chat" + ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd13b26..bfb2a1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1263,8 +1263,8 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} - baseline-browser-mapping@2.8.25: - resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==} + baseline-browser-mapping@2.9.14: + resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==} hasBin: true browserslist@4.27.0: @@ -2721,11 +2721,11 @@ snapshots: dependencies: tslib: 2.8.1 - baseline-browser-mapping@2.8.25: {} + baseline-browser-mapping@2.9.14: {} browserslist@4.27.0: dependencies: - baseline-browser-mapping: 2.8.25 + baseline-browser-mapping: 2.9.14 caniuse-lite: 1.0.30001754 electron-to-chromium: 1.5.249 node-releases: 2.0.27 diff --git a/src/pages/home/components/ai-chat-box.tsx b/src/pages/home/components/ai-chat-box.tsx new file mode 100644 index 0000000..1dc8211 --- /dev/null +++ b/src/pages/home/components/ai-chat-box.tsx @@ -0,0 +1,251 @@ +import { useState, useRef, useEffect } from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { + Sparkles, + Send, + Loader2, + User, + Bot, + MessageSquare, +} from "lucide-react"; +import { cn } from "@/lib/utils"; + +interface Message { + id: string; + role: "user" | "assistant"; + content: string; + timestamp: Date; +} + +const INITIAL_MESSAGES: Message[] = [ + { + id: "1", + role: "assistant", + content: "你好!我是你的 AI 助手。有什么我可以帮助你的吗?", + timestamp: new Date(), + }, +]; + +export function AiChatBox() { + const [messages, setMessages] = useState(INITIAL_MESSAGES); + const [input, setInput] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const scrollRef = useRef(null); + + // Auto scroll to bottom when new messages arrive + useEffect(() => { + if (scrollRef.current) { + scrollRef.current.scrollTop = scrollRef.current.scrollHeight; + } + }, [messages]); + + const handleSend = async () => { + if (!input.trim() || isLoading) return; + + const userMessage: Message = { + id: Date.now().toString(), + role: "user", + content: input.trim(), + timestamp: new Date(), + }; + + setMessages((prev) => [...prev, userMessage]); + setInput(""); + setIsLoading(true); + + // Simulate AI response + setTimeout(() => { + const aiMessage: Message = { + id: (Date.now() + 1).toString(), + role: "assistant", + content: `我收到了你的消息:"${userMessage.content}"。这是一个模拟回复。`, + timestamp: new Date(), + }; + setMessages((prev) => [...prev, aiMessage]); + setIsLoading(false); + }, 1000); + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + return ( + + + +
+ +
+ AI 助手 +
+

+ 随时为你提供帮助和建议 +

+
+ + + {/* Messages Area */} + +
+ {messages.map((message) => ( + + ))} + {isLoading && ( +
+ + + + + +
+ + + 正在思考... + +
+
+ )} +
+
+ + {/* Input Area */} +
+
+ setInput(e.target.value)} + onKeyPress={handleKeyPress} + disabled={isLoading} + className="pr-10 border-border/50" + /> + +
+ +
+ + {/* Quick Actions */} +
+ setInput("帮我总结最近的笔记")} + /> + setInput("帮我创建一个新的待办事项")} + /> + setInput("帮我搜索相关内容")} + /> +
+
+
+ ); +} + +interface MessageBubbleProps { + message: Message; +} + +function MessageBubble({ message }: MessageBubbleProps) { + const isUser = message.role === "user"; + + return ( +
+ + + {isUser ? : } + + + +
+

+ {message.content} +

+ + {message.timestamp.toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + })} + +
+
+ ); +} + +interface QuickActionButtonProps { + text: string; + onClick: () => void; +} + +function QuickActionButton({ text, onClick }: QuickActionButtonProps) { + return ( + + ); +} diff --git a/src/pages/home/components/home-header.tsx b/src/pages/home/components/home-header.tsx index 74ed53b..159b024 100644 --- a/src/pages/home/components/home-header.tsx +++ b/src/pages/home/components/home-header.tsx @@ -4,43 +4,72 @@ import { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, + DropdownMenuSeparator, } from "@/components/ui/dropdown-menu"; -import { Separator } from "@/components/ui/separator"; import { ChevronDown, Plus, Search } from "lucide-react"; +import { Input } from "@/components/ui/input"; +import { useState } from "react"; export function HomeHeader() { + const [searchQuery, setSearchQuery] = useState(""); + + const handleSearch = (e: React.FormEvent) => { + e.preventDefault(); + // TODO: 实现搜索逻辑 + console.log("Search:", searchQuery); + }; + return ( - <> -
-
-
-
- - - {/* */} -
- - - - - - 空白笔记 - 设置Todo - 从模板创建... - - 新的笔记库 - - -
-
- + + + + + 空白笔记 + + + + 从模板创建 + + + + 设置待办项 + + + + + 新的笔记库 + + + + + ); -} +} \ No newline at end of file diff --git a/src/pages/home/components/pinned-note-card.tsx b/src/pages/home/components/pinned-note-card.tsx index eebf472..b462f69 100644 --- a/src/pages/home/components/pinned-note-card.tsx +++ b/src/pages/home/components/pinned-note-card.tsx @@ -12,7 +12,14 @@ import { CarouselNext, CarouselPrevious, } from "@/components/ui/carousel"; -import { Star } from "lucide-react"; +import { Star, MoreHorizontal } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import Autoplay from "embla-carousel-autoplay"; import { useRef } from "react"; @@ -30,45 +37,99 @@ interface PinnedNoteCardProps { export function PinnedNoteCard({ notes }: PinnedNoteCardProps) { const autoplay = useRef(Autoplay({ delay: 1500, stopOnInteraction: true })); + + if (notes.length === 0) { + return ( + + + +

+ 还没有收藏内容,从你的笔记中收藏一些内容开始吧 +

+
+
+ ); + } + + return ( + + + {notes.map((note) => ( + + + + ))} + + {notes.length > 1 && ( + <> + + + + )} + + ); +} + +interface PinnedNoteItemProps { + note: PinnedNote; +} + +function PinnedNoteItem({ note }: PinnedNoteItemProps) { return ( - <> - - - {notes.map((note, index) => ( - - - -
-
- - {note.emoji} - - {note.title} -
- -
-
- - {note.description} -

- 最后编辑: {note.lastEdited} -

-
-
-
- ))} -
- - -
- + + +
+
+ + {note.emoji} + + +
+ + + + + + 编辑 + 取消收藏 + + 删除 + + + +
+ + {note.title} + +
+ + + {note.description} + +

{note.lastEdited}

+
+
); } diff --git a/src/pages/home/components/quick-stats.tsx b/src/pages/home/components/quick-stats.tsx new file mode 100644 index 0000000..3d16fef --- /dev/null +++ b/src/pages/home/components/quick-stats.tsx @@ -0,0 +1,79 @@ +import { Card, CardContent } from "@/components/ui/card"; +import { + FileText, + Star, + AlertCircle, + CheckCircle2, + Notebook, +} from "lucide-react"; + +export function QuickStatsSection() { + const stats = [ + { + label: "总笔记数", + value: "24", + icon: FileText, + color: "text-blue-500", + bgColor: "bg-blue-50 dark:bg-blue-950/30", + }, + { + label: "收藏笔记", + value: "8", + icon: Star, + color: "text-yellow-500", + bgColor: "bg-yellow-50 dark:bg-yellow-950/30", + }, + { + label: "待办项目", + value: "5", + icon: AlertCircle, + color: "text-orange-500", + bgColor: "bg-orange-50 dark:bg-orange-950/30", + }, + { + label: "已完成项目", + value: "12", + icon: CheckCircle2, + color: "text-green-500", + bgColor: "bg-green-50 dark:bg-green-950/30", + }, + ]; + + return ( +
+
+

+
+ +
+ 活动概览 +

+
+
+ {stats.map((stat) => { + const Icon = stat.icon; + return ( + + +
+
+

+ {stat.label} +

+

{stat.value}

+
+
+ +
+
+
+
+ ); + })} +
+
+ ); +} diff --git a/src/pages/home/components/recent-note-item.tsx b/src/pages/home/components/recent-note-item.tsx index e24410f..4e3d22e 100644 --- a/src/pages/home/components/recent-note-item.tsx +++ b/src/pages/home/components/recent-note-item.tsx @@ -43,6 +43,7 @@ export interface CommentActivity { time: string; } +// 清理后的模拟数据 const recentEditedNotes: NoteActivity[] = [ { id: 4, @@ -104,7 +105,7 @@ const recentComments: CommentActivity[] = [ id: 7, user: "Bob", avatar: "https://github.com/shadcn.png", - comment: "这个想法很棒 ,我们可以下周讨论一下细节。", + comment: "这个想法很棒,我们可以下周讨论一下细节。", note: "一个关于Tauri的有趣想法", time: "30分钟前", }, @@ -112,7 +113,7 @@ const recentComments: CommentActivity[] = [ id: 8, user: "Alice", avatar: "https://github.com/shadcn.png", - comment: "会议纪要已确认 ,无异议。", + comment: "会议纪要已确认,无异议。", note: "项目Alpha启动会议纪要", time: "1小时前", }, @@ -121,7 +122,7 @@ const recentComments: CommentActivity[] = [ const ActivityItemWrapper: React.FC<{ children: React.ReactNode }> = ({ children, }) => ( -
+
{children} @@ -134,18 +135,22 @@ const ActivityItemWrapper: React.FC<{ children: React.ReactNode }> = ({ - 移除记录 + 打开 添加到收藏 - 删除笔记 + 移除
); const renderPath = (path: string | { id: string; name: string }[]) => { - if (typeof path === "string") return {path}; + if (typeof path === "string") { + return ( + {path} + ); + } return ( -
+
{path.map((p, index) => ( {p.name} @@ -156,27 +161,19 @@ const renderPath = (path: string | { id: string; name: string }[]) => { ); }; -interface NoteActivityItemProps { - note: NoteActivity; -} - -const NoteActivityItem: React.FC = ({ note }) => ( +const NoteActivityItem: React.FC<{ note: NoteActivity }> = ({ note }) => ( -
- {note.emoji || "📄"} +
+ {note.emoji || "📄"}
-

{note.title}

-
- {note.path && ( -
{renderPath(note.path)}
- )} - {note.path && note.tags && note.tags.length > 0 && ( - | - )} +

{note.title}

+
+ {note.path &&
{renderPath(note.path)}
} {note.tags && note.tags.length > 0 && ( -
+
+ {note.path && } {note.tags.slice(0, 2).map((tag) => ( - + {tag} ))} @@ -185,81 +182,112 @@ const NoteActivityItem: React.FC = ({ note }) => (
-
- {note.time} - +
+ + {note.time} + + - {note.author.name.charAt(0)} + + {note.author.name.charAt(0)} +
); -interface CommentActivityItemProps { - item: CommentActivity; -} - -const CommentActivityItem: React.FC = ({ item }) => ( +const CommentActivityItem: React.FC<{ item: CommentActivity }> = ({ item }) => ( -
- +
+ {item.user.charAt(0)} -
-

+

+

{item.user}{" "} 评论了 " - {item.note}" + {item.note}"

-
- {item.comment} +
+ "{item.comment}"
- + {item.time} ); -// 主组件:最近动态 export const RecentActivitySection: React.FC = () => ( -
-

- +
+

+
+ +
最近动态

- - - 最近编辑 + + + + 最近编辑 + 编辑 - - 最近浏览 + + + 最近浏览 + 浏览 - - 最近评论 + + + 最近评论 + 评论 - - + + - {recentEditedNotes.map((note) => ( - - ))} + {recentEditedNotes.length > 0 ? ( +
+ {recentEditedNotes.map((note) => ( + + ))} +
+ ) : ( +
+ 暂无最近编辑的笔记 +
+ )}
- {recentViewedNotes.map((note) => ( - - ))} + {recentViewedNotes.length > 0 ? ( +
+ {recentViewedNotes.map((note) => ( + + ))} +
+ ) : ( +
+ 暂无最近浏览的笔记 +
+ )}
- {recentComments.map((item) => ( - - ))} + {recentComments.length > 0 ? ( +
+ {recentComments.map((item) => ( + + ))} +
+ ) : ( +
+ 暂无最近评论 +
+ )}
diff --git a/src/pages/home/components/welcome-section.tsx b/src/pages/home/components/welcome-section.tsx new file mode 100644 index 0000000..ca44c44 --- /dev/null +++ b/src/pages/home/components/welcome-section.tsx @@ -0,0 +1,39 @@ +import { useEffect, useState } from "react"; + +export function WelcomeSection() { + const greeting = getGreeting(); + const userName = "Alex"; + const [isWaving, setIsWaving] = useState(true); + + useEffect(() => { + const timer = setTimeout(() => { + setIsWaving(false); + }, 1000); // 1秒后停止挥动 + return () => clearTimeout(timer); + }, []); + + return ( +
+

+ {greeting} {userName}!{" "} + + 👋 + +

+

+ 今天是个创造的好日子。 +

+
+ ); +} + +function getGreeting(): string { + const hour = new Date().getHours(); + if (hour < 12) return "早上好"; + if (hour < 18) return "下午好"; + return "晚上好"; +} diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 26c11b7..bfa76da 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -2,8 +2,10 @@ import { Star } from "lucide-react"; import { PinnedNoteCard } from "./components/pinned-note-card"; import { RecentActivitySection } from "./components/recent-note-item"; import { HomeHeader } from "./components/home-header"; +import { QuickStatsSection } from "./components/quick-stats"; +import { WelcomeSection } from "./components/welcome-section"; +import { AiChatBox } from "./components/ai-chat-box"; -// --- 模拟数据 --- const pinnedNotes = [ { id: 1, @@ -53,20 +55,39 @@ export function HomePage() { return (
-
- -
-
-

👋 欢迎回来, Alex!

-

- 今天是个创造的好日子。 -

+ {/* Header */} + + + {/* Welcome Section */} + + + {/* Quick Stats */} + + + {/* AI Chat Box */} +
+
-
-

- 收藏内容 -

- + + {/* Main Content */} +
+ {/* Pinned Notes Section */} +
+
+

+
+ +
+ 收藏内容 +

+
+ +
+ + {/* Recent Activity Section */}
diff --git a/src/style.css b/src/style.css index 7c4f2aa..4740500 100644 --- a/src/style.css +++ b/src/style.css @@ -137,3 +137,15 @@ html { width: 0px; /* 设置滚动条宽度 */ height: 0px; /* 设置水平滚动条高度 */ } + +/* Wave animation for welcome section */ +@keyframes wave { + 0%, 100% { transform: rotate(0deg); } + 25% { transform: rotate(15deg); } + 50% { transform: rotate(-10deg); } + 75% { transform: rotate(10deg); } +} + +.wave-animation { + animation: wave 0.6s ease-in-out 2; +}