'use client' import { useEffect, useRef, useState } from 'react' import { IconBulb as Bulb, IconPencil as Pencil, IconScale as Scale, IconSearch as Search, IconSettings as Settings, IconTool as Tool, type TablerIcon } from '@tabler/icons-react' import { captureClient } from '@/lib/analytics/posthog-client' import { cn } from '@/lib/utils' import { Button } from './ui/button' // Constants for timing delays const FOCUS_OUT_DELAY_MS = 100 // Delay to ensure focus has actually moved interface ActionCategory { icon: TablerIcon label: string key: string } const actionCategories: ActionCategory[] = [ { icon: Scale, label: 'Decide', key: 'decide' }, { icon: Tool, label: 'Troubleshoot', key: 'troubleshoot' }, { icon: Settings, label: 'How-to', key: 'howto' }, { icon: Bulb, label: 'Understand', key: 'understand' }, { icon: Pencil, label: 'Create', key: 'create' } ] // Onboarding examples are tuned to showcase grounded, GenUI-rich answers // (images, comparison tables, structured depth) for concrete, self-contained // tasks — the patterns that correlate with follow-up in real usage. Create // mirrors what people write in drafts. Keep each example self-contained (no // "my notes"/"this file" referencing absent context). const promptSamples: Record = { troubleshoot: [ 'My car starts then immediately stalls, but the electronics still work', 'Wi-Fi keeps dropping on one laptop but not my phone — how do I fix it?', "My sourdough starter isn't rising after a week — what's wrong?", 'Next.js build fails with "Module not found" only in production' ], howto: [ 'Move my photos off Google Photos without losing albums', 'Set up a Proxmox home server for self-hosting', 'Convert a folder of .txt files to clean HTML', 'Set up a Plex media server to stream my movies' ], decide: [ 'Tesla vs Rivian — which should I buy?', 'Standing vs sitting desk for lower-back pain — which and why?', 'A budget mirrorless camera for travel under $1,000', 'Notion vs Obsidian for a personal knowledge base' ], understand: [ 'What causes the northern lights?', 'Why did the dinosaurs really go extinct?', 'How does a nuclear reactor actually generate electricity?', 'How do noise-cancelling headphones actually work?' ], create: [ 'Write a polite email asking my landlord to fix the broken heater this week', 'Write an Instagram post announcing a bakery’s new autumn menu', 'Write a short cover letter for a part-time barista job at a local café', 'Write a text-to-video prompt for a 10-second clip of a cat surfing at sunset' ] } interface ActionButtonsProps { onSelectPrompt: (prompt: string) => void onCategoryClick: (category: string) => void inputRef?: React.RefObject className?: string } export function ActionButtons({ onSelectPrompt, onCategoryClick, inputRef, className }: ActionButtonsProps) { const [activeCategory, setActiveCategory] = useState(null) const containerRef = useRef(null) const handleCategoryClick = (category: ActionCategory) => { setActiveCategory(category.key) onCategoryClick(category.label) captureClient('example_category_opened', { category: category.key }) } const handlePromptClick = (prompt: string) => { captureClient('example_prompt_clicked', { category: activeCategory, prompt }) setActiveCategory(null) onSelectPrompt(prompt) } const resetToButtons = () => { setActiveCategory(null) } // Handle Escape key and clicks outside (including focus loss) useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape' && activeCategory) { resetToButtons() } } const handleClickOutside = (e: MouseEvent) => { if ( containerRef.current && !containerRef.current.contains(e.target as Node) ) { if (activeCategory) { // Check if click is not on the input field if (!inputRef?.current?.contains(e.target as Node)) { resetToButtons() } } } } const handleFocusOut = () => { // Check if focus is moving outside both the container and input setTimeout(() => { const activeElement = document.activeElement if ( activeCategory && !containerRef.current?.contains(activeElement) && activeElement !== inputRef?.current ) { resetToButtons() } }, FOCUS_OUT_DELAY_MS) } document.addEventListener('keydown', handleEscape) document.addEventListener('mousedown', handleClickOutside) document.addEventListener('focusout', handleFocusOut) return () => { document.removeEventListener('keydown', handleEscape) document.removeEventListener('mousedown', handleClickOutside) document.removeEventListener('focusout', handleFocusOut) } }, [activeCategory, inputRef]) // Max height for samples (4 items up to 2 lines each + padding); overflow scrolls const containerHeight = 'h-[232px]' return (
{/* Action buttons */}
{actionCategories.map(category => { const Icon = category.icon return ( ) })}
{/* Prompt samples */}
{activeCategory && promptSamples[activeCategory]?.map((prompt, index) => ( ))}
) }