diff --git a/frontend/src/components/ai-elements/suggestion.tsx b/frontend/src/components/ai-elements/suggestion.tsx index 9d76a82..91cd0c4 100644 --- a/frontend/src/components/ai-elements/suggestion.tsx +++ b/frontend/src/components/ai-elements/suggestion.tsx @@ -1,11 +1,10 @@ "use client"; import { Button } from "@/components/ui/button"; -import { - ScrollArea, - ScrollBar, -} from "@/components/ui/scroll-area"; +import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; import { cn } from "@/lib/utils"; +import { Icon } from "@radix-ui/react-select"; +import type { LucideIcon } from "lucide-react"; import type { ComponentProps } from "react"; export type SuggestionsProps = ComponentProps; @@ -15,7 +14,7 @@ export const Suggestions = ({ children, ...props }: SuggestionsProps) => ( - +
{children}
@@ -24,32 +23,38 @@ export const Suggestions = ({ ); export type SuggestionProps = Omit, "onClick"> & { - suggestion: string; - onClick?: (suggestion: string) => void; + suggestion: React.ReactNode; + icon?: LucideIcon; + onClick?: () => void; }; export const Suggestion = ({ suggestion, onClick, className, + icon: Icon, variant = "outline", size = "sm", children, ...props }: SuggestionProps) => { const handleClick = () => { - onClick?.(suggestion); + onClick?.(); }; return ( ); diff --git a/frontend/src/components/workspace/input-box.tsx b/frontend/src/components/workspace/input-box.tsx index 4a123b7..21e09bc 100644 --- a/frontend/src/components/workspace/input-box.tsx +++ b/frontend/src/components/workspace/input-box.tsx @@ -6,13 +6,13 @@ import { GraduationCapIcon, LightbulbIcon, PaperclipIcon, + PlusIcon, ZapIcon, } from "lucide-react"; import { useCallback, useMemo, useState, type ComponentProps } from "react"; import { PromptInput, - PromptInputActionAddAttachments, PromptInputActionMenu, PromptInputActionMenuContent, PromptInputActionMenuItem, @@ -26,11 +26,13 @@ import { PromptInputTextarea, PromptInputTools, usePromptInputAttachments, + usePromptInputController, type PromptInputMessage, } from "@/components/ai-elements/prompt-input"; import { DropdownMenuGroup, DropdownMenuLabel, + DropdownMenuSeparator, } from "@/components/ui/dropdown-menu"; import { useI18n } from "@/core/i18n/hooks"; import { useModels } from "@/core/models/hooks"; @@ -46,6 +48,13 @@ import { ModelSelectorName, ModelSelectorTrigger, } from "../ai-elements/model-selector"; +import { Suggestion, Suggestions } from "../ai-elements/suggestion"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "../ui/dropdown-menu"; import { Tooltip } from "./tooltip"; @@ -338,6 +347,11 @@ export function InputBox({ /> + {isNewThread && ( +
+ +
+ )} {!isNewThread && (
)} @@ -345,6 +359,67 @@ export function InputBox({ ); } +function SuggestionList() { + const { t } = useI18n(); + const { textInput } = usePromptInputController(); + const handleSuggestionClick = useCallback( + (prompt: string | undefined) => { + if (!prompt) return; + textInput.setInput(prompt); + setTimeout(() => { + const textarea = document.querySelector( + "textarea[name='message']", + ); + if (textarea) { + const selStart = prompt.indexOf("["); + const selEnd = prompt.indexOf("]"); + if (selStart !== -1 && selEnd !== -1) { + textarea.setSelectionRange(selStart, selEnd + 1); + textarea.focus(); + } + } + }, 500); + }, + [textInput], + ); + return ( + + {t.inputBox.suggestions.map((suggestion) => ( + handleSuggestionClick(suggestion.prompt)} + /> + ))} + + + + + + + {t.inputBox.suggestionsCreate.map((suggestion, index) => + "type" in suggestion && suggestion.type === "separator" ? ( + + ) : ( + !("type" in suggestion) && ( + handleSuggestionClick(suggestion.prompt)} + > + {suggestion.icon && } + {suggestion.suggestion} + + ) + ), + )} + + + + + ); +} + function AddAttachmentsButton({ className }: { className?: string }) { const { t } = useI18n(); const attachments = usePromptInputAttachments(); diff --git a/frontend/src/core/i18n/locales/en-US.ts b/frontend/src/core/i18n/locales/en-US.ts index abab5a8..613d31e 100644 --- a/frontend/src/core/i18n/locales/en-US.ts +++ b/frontend/src/core/i18n/locales/en-US.ts @@ -1,3 +1,14 @@ +import { + CompassIcon, + GraduationCapIcon, + ImageIcon, + MicroscopeIcon, + PenLineIcon, + ShapesIcon, + SparklesIcon, + VideoIcon, +} from "lucide-react"; + import type { Translations } from "./types"; export const enUS: Translations = { @@ -29,6 +40,7 @@ export const enUS: Translations = { cancel: "Cancel", save: "Save", install: "Install", + create: "Create", }, // Welcome @@ -62,6 +74,55 @@ export const enUS: Translations = { proModeDescription: "Reasoning, planning and executing, get more accurate results, may take more time", searchModels: "Search models...", + suggestions: [ + { + suggestion: "Write", + prompt: "Write a blog post about the latest trends on [topic]", + icon: PenLineIcon, + }, + { + suggestion: "Research", + prompt: + "Conduct a deep dive research on [topic], and summarize the findings.", + icon: MicroscopeIcon, + }, + { + suggestion: "Collect", + prompt: "Collect data from [source] and create a report.", + icon: ShapesIcon, + }, + { + suggestion: "Learn", + prompt: "Learn about [topic] and create a tutorial.", + icon: GraduationCapIcon, + }, + ], + suggestionsCreate: [ + { + suggestion: "Webpage", + prompt: "Create a webpage about [topic]", + icon: CompassIcon, + }, + { + suggestion: "Image", + prompt: "Create an image about [topic]", + icon: ImageIcon, + }, + { + suggestion: "Video", + prompt: "Create a video about [topic]", + icon: VideoIcon, + }, + { + type: "separator", + }, + { + suggestion: "Skill", + prompt: + "We're going to build a new skill step by step with `skill-creator`. To start, what do you want this skill to do?", + icon: SparklesIcon, + }, + ], }, // Sidebar diff --git a/frontend/src/core/i18n/locales/types.ts b/frontend/src/core/i18n/locales/types.ts index db6bc45..169d768 100644 --- a/frontend/src/core/i18n/locales/types.ts +++ b/frontend/src/core/i18n/locales/types.ts @@ -1,3 +1,5 @@ +import type { LucideIcon } from "lucide-react"; + export interface Translations { // Locale meta locale: { @@ -27,6 +29,7 @@ export interface Translations { cancel: string; save: string; install: string; + create: string; }; // Welcome @@ -56,6 +59,21 @@ export interface Translations { proMode: string; proModeDescription: string; searchModels: string; + suggestions: { + suggestion: string; + prompt: string; + icon: LucideIcon; + }[]; + suggestionsCreate: ( + | { + suggestion: string; + prompt: string; + icon: LucideIcon; + } + | { + type: "separator"; + } + )[]; }; // Sidebar diff --git a/frontend/src/core/i18n/locales/zh-CN.ts b/frontend/src/core/i18n/locales/zh-CN.ts index 59442e8..16713d8 100644 --- a/frontend/src/core/i18n/locales/zh-CN.ts +++ b/frontend/src/core/i18n/locales/zh-CN.ts @@ -1,3 +1,14 @@ +import { + CompassIcon, + GraduationCapIcon, + ImageIcon, + MicroscopeIcon, + PenLineIcon, + ShapesIcon, + SparklesIcon, + VideoIcon, +} from "lucide-react"; + import type { Translations } from "./types"; export const zhCN: Translations = { @@ -29,6 +40,7 @@ export const zhCN: Translations = { cancel: "取消", save: "保存", install: "安装", + create: "创建", }, // Welcome @@ -60,6 +72,54 @@ export const zhCN: Translations = { proMode: "专业", proModeDescription: "思考、计划再执行,获得更精准的结果,可能需要更多时间", searchModels: "搜索模型...", + suggestions: [ + { + suggestion: "写作", + prompt: "撰写一篇关于[主题]的博客文章", + icon: PenLineIcon, + }, + { + suggestion: "研究", + prompt: "深入浅出的研究一下[主题],并总结发现。", + icon: MicroscopeIcon, + }, + { + suggestion: "收集", + prompt: "从[来源]收集数据并创建报告。", + icon: ShapesIcon, + }, + { + suggestion: "学习", + prompt: "学习关于[主题]并创建教程。", + icon: GraduationCapIcon, + }, + ], + suggestionsCreate: [ + { + suggestion: "网页", + prompt: "生成一个关于[主题]的网页", + icon: CompassIcon, + }, + { + suggestion: "图片", + prompt: "生成一个关于[主题]的图片", + icon: ImageIcon, + }, + { + suggestion: "视频", + prompt: "生成一个关于[主题]的视频", + icon: VideoIcon, + }, + { + type: "separator", + }, + { + suggestion: "技能", + prompt: + "我们一起用 skill-creator 技能来创建一个技能吧。先问问我希望这个技能能做什么。", + icon: SparklesIcon, + }, + ], }, // Sidebar