From 9ca143aa463ac16b6602fc875b13855fc22256b9 Mon Sep 17 00:00:00 2001 From: Arjun <6592213+arkml@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:00:31 +0530 Subject: [PATCH] added prompt suggestions --- apps/x/apps/renderer/src/App.tsx | 25 ++++++ .../components/ai-elements/suggestions.tsx | 76 +++++++++++++++++++ .../renderer/src/components/chat-sidebar.tsx | 11 +++ 3 files changed, 112 insertions(+) create mode 100644 apps/x/apps/renderer/src/components/ai-elements/suggestions.tsx diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index fe0089e7..8bb42a42 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -38,6 +38,7 @@ import { Shimmer } from '@/components/ai-elements/shimmer'; import { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput } from '@/components/ai-elements/tool'; import { PermissionRequest } from '@/components/ai-elements/permission-request'; import { AskHumanRequest } from '@/components/ai-elements/ask-human-request'; +import { Suggestions } from '@/components/ai-elements/suggestions'; import { ToolPermissionRequestEvent, AskHumanRequestEvent } from '@x/shared/src/runs.js'; import { SidebarInset, @@ -278,16 +279,28 @@ const collectFilePaths = (nodes: TreeNode[]): string[] => interface ChatInputInnerProps { onSubmit: (message: PromptInputMessage, mentions?: FileMention[]) => void isProcessing: boolean + presetMessage?: string + onPresetMessageConsumed?: () => void } function ChatInputInner({ onSubmit, isProcessing, + presetMessage, + onPresetMessageConsumed, }: ChatInputInnerProps) { const controller = usePromptInputController() const message = controller.textInput.value const canSubmit = Boolean(message.trim()) && !isProcessing + // Handle preset message from suggestions + useEffect(() => { + if (presetMessage) { + controller.textInput.setInput(presetMessage) + onPresetMessageConsumed?.() + } + }, [presetMessage, controller.textInput, onPresetMessageConsumed]) + const handleSubmit = useCallback(() => { if (!canSubmit) return onSubmit({ text: message.trim(), files: [] }, controller.mentions.mentions) @@ -334,6 +347,8 @@ interface ChatInputWithMentionsProps { visibleFiles: string[] onSubmit: (message: PromptInputMessage, mentions?: FileMention[]) => void isProcessing: boolean + presetMessage?: string + onPresetMessageConsumed?: () => void } function ChatInputWithMentions({ @@ -342,12 +357,16 @@ function ChatInputWithMentions({ visibleFiles, onSubmit, isProcessing, + presetMessage, + onPresetMessageConsumed, }: ChatInputWithMentionsProps) { return ( ) @@ -386,6 +405,7 @@ function App() { const [runId, setRunId] = useState(null) const [isProcessing, setIsProcessing] = useState(false) const [agentId] = useState('copilot') + const [presetMessage, setPresetMessage] = useState(undefined) // Runs history state type RunListItem = { id: string; title?: string; createdAt: string; agentId: string } @@ -1598,12 +1618,17 @@ function App() {
+ {!hasConversation && ( + + )} setPresetMessage(undefined)} />
diff --git a/apps/x/apps/renderer/src/components/ai-elements/suggestions.tsx b/apps/x/apps/renderer/src/components/ai-elements/suggestions.tsx new file mode 100644 index 00000000..cd07ed7a --- /dev/null +++ b/apps/x/apps/renderer/src/components/ai-elements/suggestions.tsx @@ -0,0 +1,76 @@ +import { Mail, Calendar, FolderOpen, FileText } from 'lucide-react' +import { cn } from '@/lib/utils' + +export interface Suggestion { + id: string + label: string + prompt: string + icon: React.ReactNode +} + +const defaultSuggestions: Suggestion[] = [ + { + id: 'email-draft', + label: 'Draft an email', + prompt: "Let's draft an email response to [name]", + icon: , + }, + { + id: 'meeting-prep', + label: 'Prep for a meeting', + prompt: 'Help me prep for my next meeting with [name]', + icon: , + }, + { + id: 'doc-collab', + label: 'Work on a document', + prompt: "Let's work on [document name]", + icon: , + }, + { + id: 'organize-files', + label: 'Organize files', + prompt: 'Help me organize [folder or files]', + icon: , + }, +] + +interface SuggestionsProps { + suggestions?: Suggestion[] + onSelect: (prompt: string) => void + className?: string + vertical?: boolean +} + +export function Suggestions({ + suggestions = defaultSuggestions, + onSelect, + className, + vertical = false, +}: SuggestionsProps) { + return ( +
+ {suggestions.map((suggestion) => ( + + ))} +
+ ) +} diff --git a/apps/x/apps/renderer/src/components/chat-sidebar.tsx b/apps/x/apps/renderer/src/components/chat-sidebar.tsx index 5c5f7713..0d9a7d7e 100644 --- a/apps/x/apps/renderer/src/components/chat-sidebar.tsx +++ b/apps/x/apps/renderer/src/components/chat-sidebar.tsx @@ -23,6 +23,7 @@ import { Shimmer } from '@/components/ai-elements/shimmer' import { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput } from '@/components/ai-elements/tool' import { PermissionRequest } from '@/components/ai-elements/permission-request' import { AskHumanRequest } from '@/components/ai-elements/ask-human-request' +import { Suggestions } from '@/components/ai-elements/suggestions' import { type PromptInputMessage, type FileMention } from '@/components/ai-elements/prompt-input' import { useMentionDetection } from '@/hooks/use-mention-detection' import { MentionPopover } from '@/components/mention-popover' @@ -544,6 +545,16 @@ export function ChatSidebar({ {/* Input area - responsive to sidebar width, matches floating bar position exactly */}
+ {!hasConversation && ( + { + onMessageChange(prompt) + setTimeout(() => textareaRef.current?.focus(), 0) + }} + vertical + className="mb-3" + /> + )}
{mentionHighlights.hasHighlights && (