mirror of
https://github.com/johndoe6345789/snippet-pastebin.git
synced 2026-04-24 21:44:54 +00:00
Generated by Spark: AI button for errors.
This commit is contained in:
12
PRD.md
12
PRD.md
@@ -54,6 +54,13 @@ This is a CRUD application with search, filtering, and organization features but
|
||||
- **Progression**: Click copy button → Visual feedback (toast notification) → Paste code elsewhere
|
||||
- **Success criteria**: Code copies exactly as stored, toast confirms action, works across browsers
|
||||
|
||||
### AI Error Helper
|
||||
- **Functionality**: Intelligent error analysis button that appears when errors occur, uses AI to explain errors in plain language and suggest fixes
|
||||
- **Purpose**: Help developers quickly understand and resolve errors without leaving the app
|
||||
- **Trigger**: Automatically appears when runtime errors, preview rendering errors, or validation errors occur
|
||||
- **Progression**: Error occurs → AI helper button appears with pulsing animation → Click button → AI analyzes error context → Display explanation and suggested fixes in dialog
|
||||
- **Success criteria**: Button appears within 100ms of error, AI provides helpful context-aware explanations, suggestions are actionable and specific to the error type
|
||||
|
||||
## Edge Case Handling
|
||||
- **Empty State**: Show welcoming illustration and "Create your first snippet" CTA when no snippets exist
|
||||
- **Long Code Blocks**: Truncate preview with "Show more" expansion, scroll within card for full view
|
||||
@@ -61,8 +68,9 @@ This is a CRUD application with search, filtering, and organization features but
|
||||
- **Invalid Input**: Require title and code content minimum, show inline validation errors
|
||||
- **Search No Results**: Display "No snippets found" message with suggestion to adjust filters
|
||||
- **Network/Storage Errors**: Graceful error messages with retry options (though KV storage is local)
|
||||
- **Preview Rendering Errors**: Display detailed error messages when React code fails to compile or render, show warnings for non-React language previews
|
||||
- **Preview Rendering Errors**: Display detailed error messages when React code fails to compile or render, show warnings for non-React language previews, AI helper button available for error analysis
|
||||
- **Preview Not Available**: Show informative message for snippets without preview enabled or non-React languages
|
||||
- **AI Error Analysis Failure**: If AI service is unavailable, show graceful fallback message with standard error details
|
||||
|
||||
## Design Direction
|
||||
The design should evoke a premium developer tool - clean, focused, and sophisticated with subtle tech-forward aesthetics. Think VS Code meets Notion: professional minimalism with purposeful color accents and smooth micro-interactions that make frequent use satisfying.
|
||||
@@ -106,6 +114,7 @@ Animations should feel responsive and technical - quick, purposeful movements th
|
||||
- AlertDialog for delete confirmations
|
||||
- Checkbox for enabling split-screen preview mode
|
||||
- Alert for displaying preview rendering errors
|
||||
- Custom AI helper button with sparkle/magic wand icon for error analysis
|
||||
|
||||
- **Customizations**:
|
||||
- Custom syntax language badges with predefined color mapping (JavaScript=yellow, Python=blue, JSX/TSX=cyan/sky, etc.)
|
||||
@@ -130,6 +139,7 @@ Animations should feel responsive and technical - quick, purposeful movements th
|
||||
- Check (confirmation feedback)
|
||||
- SplitVertical (preview mode indicator and toggle)
|
||||
- WarningCircle (preview errors)
|
||||
- Sparkles/MagicWand (AI helper for error analysis)
|
||||
|
||||
- **Spacing**:
|
||||
- Container padding: p-6 (desktop) / p-4 (mobile)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"templateVersion": 0,
|
||||
}
|
||||
{
|
||||
"templateVersion": 0,
|
||||
"dbType": null
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Alert, AlertTitle, AlertDescription } from "./components/ui/alert";
|
||||
import { Button } from "./components/ui/button";
|
||||
import { AIErrorHelper } from "./components/AIErrorHelper";
|
||||
|
||||
import { AlertTriangleIcon, RefreshCwIcon } from "lucide-react";
|
||||
|
||||
export const ErrorFallback = ({ error, resetErrorBoundary }) => {
|
||||
// When encountering an error in the development mode, rethrow it and don't display the boundary.
|
||||
// The parent UI will take care of showing a more helpful dialog.
|
||||
if (import.meta.env.DEV) throw error;
|
||||
|
||||
return (
|
||||
@@ -26,14 +25,18 @@ export const ErrorFallback = ({ error, resetErrorBoundary }) => {
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={resetErrorBoundary}
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
>
|
||||
<RefreshCwIcon />
|
||||
Try Again
|
||||
</Button>
|
||||
<div className="flex flex-col gap-3">
|
||||
<AIErrorHelper error={error} context="Application runtime error" />
|
||||
|
||||
<Button
|
||||
onClick={resetErrorBoundary}
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
>
|
||||
<RefreshCwIcon />
|
||||
Try Again
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
189
src/components/AIErrorHelper.tsx
Normal file
189
src/components/AIErrorHelper.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Sparkle } from '@phosphor-icons/react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
|
||||
interface AIErrorHelperProps {
|
||||
error: Error | string
|
||||
context?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function AIErrorHelper({ error, context, className }: AIErrorHelperProps) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [analysis, setAnalysis] = useState<string>('')
|
||||
const [isAnalyzing, setIsAnalyzing] = useState(false)
|
||||
const [analysisError, setAnalysisError] = useState<string>('')
|
||||
|
||||
const errorMessage = typeof error === 'string' ? error : error.message
|
||||
const errorStack = typeof error === 'string' ? '' : error.stack
|
||||
|
||||
const analyzeError = async () => {
|
||||
setOpen(true)
|
||||
setIsAnalyzing(true)
|
||||
setAnalysisError('')
|
||||
setAnalysis('')
|
||||
|
||||
try {
|
||||
const contextInfo = context ? `\n\nContext: ${context}` : ''
|
||||
const stackInfo = errorStack ? `\n\nStack trace: ${errorStack}` : ''
|
||||
|
||||
const promptText = `You are a helpful debugging assistant for a code snippet manager app. Analyze this error and provide:
|
||||
|
||||
1. A clear explanation of what went wrong (in plain language)
|
||||
2. Why this error likely occurred
|
||||
3. 2-3 specific actionable steps to fix it
|
||||
|
||||
Error message: ${errorMessage}${contextInfo}${stackInfo}
|
||||
|
||||
Keep your response concise, friendly, and focused on practical solutions. Format your response with clear sections using markdown.`
|
||||
|
||||
const result = await window.spark.llm(promptText, 'gpt-4o-mini')
|
||||
setAnalysis(result)
|
||||
} catch (err) {
|
||||
setAnalysisError('Unable to analyze error. The AI service may be temporarily unavailable.')
|
||||
} finally {
|
||||
setIsAnalyzing(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className={className}
|
||||
>
|
||||
<Button
|
||||
onClick={analyzeError}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2 border-accent/50 text-accent hover:bg-accent/10 hover:text-accent hover:border-accent transition-all"
|
||||
>
|
||||
<motion.div
|
||||
animate={{ rotate: [0, 10, -10, 10, 0] }}
|
||||
transition={{ duration: 2, repeat: Infinity, repeatDelay: 1 }}
|
||||
>
|
||||
<Sparkle className="h-4 w-4" weight="fill" />
|
||||
</motion.div>
|
||||
Ask AI for Help
|
||||
</Button>
|
||||
</motion.div>
|
||||
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="max-w-2xl max-h-[80vh] overflow-hidden flex flex-col">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Sparkle className="h-5 w-5 text-accent" weight="fill" />
|
||||
AI Error Analysis
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Let me help you understand and fix this error
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex-1 overflow-y-auto space-y-4">
|
||||
<Alert className="bg-destructive/10 border-destructive/30">
|
||||
<AlertDescription className="text-sm font-mono">
|
||||
{errorMessage}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
{isAnalyzing && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
|
||||
>
|
||||
<Sparkle className="h-4 w-4" weight="fill" />
|
||||
</motion.div>
|
||||
<span className="text-sm">Analyzing error...</span>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
initial={{ opacity: 0.3 }}
|
||||
animate={{ opacity: [0.3, 0.6, 0.3] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity, delay: i * 0.2 }}
|
||||
className="h-4 bg-muted rounded"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{analysisError && (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>{analysisError}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<AnimatePresence>
|
||||
{analysis && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="prose prose-invert prose-sm max-w-none"
|
||||
>
|
||||
<div className="bg-card/50 rounded-lg p-4 border border-border space-y-3">
|
||||
{analysis.split('\n').map((line, idx) => {
|
||||
if (line.startsWith('###')) {
|
||||
return (
|
||||
<h3 key={idx} className="text-base font-semibold text-foreground mt-4 mb-2">
|
||||
{line.replace('###', '').trim()}
|
||||
</h3>
|
||||
)
|
||||
}
|
||||
if (line.startsWith('##')) {
|
||||
return (
|
||||
<h2 key={idx} className="text-lg font-semibold text-foreground mt-4 mb-2">
|
||||
{line.replace('##', '').trim()}
|
||||
</h2>
|
||||
)
|
||||
}
|
||||
if (line.match(/^\d+\./)) {
|
||||
return (
|
||||
<div key={idx} className="text-foreground/90 ml-2">
|
||||
{line}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (line.startsWith('-')) {
|
||||
return (
|
||||
<div key={idx} className="text-foreground/90 ml-4">
|
||||
{line}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (line.trim()) {
|
||||
return (
|
||||
<p key={idx} className="text-foreground/80 text-sm leading-relaxed">
|
||||
{line}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useState, useRef } from 'react'
|
||||
import * as React from 'react'
|
||||
import * as ReactDOM from 'react-dom'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { AIErrorHelper } from '@/components/AIErrorHelper'
|
||||
import { WarningCircle } from '@phosphor-icons/react'
|
||||
|
||||
interface ReactPreviewProps {
|
||||
@@ -77,12 +78,16 @@ export function ReactPreview({ code, language }: ReactPreviewProps) {
|
||||
if (error) {
|
||||
return (
|
||||
<div className="h-full overflow-auto p-6 bg-destructive/5">
|
||||
<Alert variant="destructive">
|
||||
<Alert variant="destructive" className="mb-4">
|
||||
<WarningCircle className="h-4 w-4" />
|
||||
<AlertDescription className="font-mono text-xs whitespace-pre-wrap">
|
||||
{error}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<AIErrorHelper
|
||||
error={error}
|
||||
context={`React component preview rendering (Language: ${language})`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user