From 5b395dcfc07b8f120da047447e697f997be5f845 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sat, 17 Jan 2026 17:23:16 +0000 Subject: [PATCH] Generated by Spark: Create new snippet and Edit snippet should allow you to configure the input parameters, incl what function gets run (function name) and function arguments. --- src/components/ReactPreview.tsx | 38 +++++- src/components/SnippetDialog.tsx | 169 ++++++++++++++++++++++++++- src/components/SnippetViewer.tsx | 7 +- src/components/SplitScreenEditor.tsx | 21 +++- src/lib/types.ts | 33 ++++-- 5 files changed, 248 insertions(+), 20 deletions(-) diff --git a/src/components/ReactPreview.tsx b/src/components/ReactPreview.tsx index 97985a1..708d990 100644 --- a/src/components/ReactPreview.tsx +++ b/src/components/ReactPreview.tsx @@ -4,13 +4,16 @@ import * as ReactDOM from 'react-dom' import { Alert, AlertDescription } from '@/components/ui/alert' import { AIErrorHelper } from '@/components/AIErrorHelper' import { WarningCircle } from '@phosphor-icons/react' +import { InputParameter } from '@/lib/types' interface ReactPreviewProps { code: string language: string + functionName?: string + inputParameters?: InputParameter[] } -export function ReactPreview({ code, language }: ReactPreviewProps) { +export function ReactPreview({ code, language, functionName, inputParameters }: ReactPreviewProps) { const [error, setError] = useState(null) const [Component, setComponent] = useState(null) const mountRef = useRef(null) @@ -43,8 +46,10 @@ export function ReactPreview({ code, language }: ReactPreviewProps) { ${transformedCode} + ${functionName ? `return ${functionName};` : ` const lastStatement = (${transformedCode.trim().split('\n').pop()}); return lastStatement; + `} }) ` @@ -61,7 +66,34 @@ export function ReactPreview({ code, language }: ReactPreviewProps) { } catch (err) { setError(err instanceof Error ? err.message : 'Failed to render preview') } - }, [code, language]) + }, [code, language, functionName]) + + const props = React.useMemo(() => { + if (!inputParameters || inputParameters.length === 0) { + return {} + } + + const parsedProps: Record = {} + + inputParameters.forEach((param) => { + try { + if (param.type === 'string') { + parsedProps[param.name] = param.defaultValue.replace(/^["']|["']$/g, '') + } else if (param.type === 'number') { + parsedProps[param.name] = Number(param.defaultValue) + } else if (param.type === 'boolean') { + parsedProps[param.name] = param.defaultValue === 'true' + } else if (param.type === 'array' || param.type === 'object') { + parsedProps[param.name] = JSON.parse(param.defaultValue) + } + } catch (err) { + console.warn(`Failed to parse parameter ${param.name}:`, err) + parsedProps[param.name] = param.defaultValue + } + }) + + return parsedProps + }, [inputParameters]) if (!['JSX', 'TSX', 'JavaScript', 'TypeScript'].includes(language)) { return ( @@ -105,7 +137,7 @@ export function ReactPreview({ code, language }: ReactPreviewProps) { return (
- +
) diff --git a/src/components/SnippetDialog.tsx b/src/components/SnippetDialog.tsx index cddefd4..5386ba7 100644 --- a/src/components/SnippetDialog.tsx +++ b/src/components/SnippetDialog.tsx @@ -19,10 +19,12 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select' -import { Snippet } from '@/lib/types' +import { Snippet, InputParameter } from '@/lib/types' import { MonacoEditor } from '@/components/MonacoEditor' import { SplitScreenEditor } from '@/components/SplitScreenEditor' import { strings, appConfig, LANGUAGES } from '@/lib/config' +import { Plus, Trash } from '@phosphor-icons/react' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' interface SnippetDialogProps { open: boolean @@ -37,6 +39,8 @@ export function SnippetDialog({ open, onOpenChange, onSave, editingSnippet }: Sn const [language, setLanguage] = useState(appConfig.defaultLanguage) const [code, setCode] = useState('') const [hasPreview, setHasPreview] = useState(false) + const [functionName, setFunctionName] = useState('') + const [inputParameters, setInputParameters] = useState([]) const [errors, setErrors] = useState<{ title?: string; code?: string }>({}) useEffect(() => { @@ -46,12 +50,16 @@ export function SnippetDialog({ open, onOpenChange, onSave, editingSnippet }: Sn setLanguage(editingSnippet.language) setCode(editingSnippet.code) setHasPreview(editingSnippet.hasPreview || false) + setFunctionName(editingSnippet.functionName || '') + setInputParameters(editingSnippet.inputParameters || []) } else { setTitle('') setDescription('') setLanguage(appConfig.defaultLanguage) setCode('') setHasPreview(false) + setFunctionName('') + setInputParameters([]) } setErrors({}) }, [editingSnippet, open]) @@ -78,6 +86,8 @@ export function SnippetDialog({ open, onOpenChange, onSave, editingSnippet }: Sn code: code.trim(), category: editingSnippet?.category || 'general', hasPreview, + functionName: functionName.trim() || undefined, + inputParameters: inputParameters.length > 0 ? inputParameters : undefined, }) setTitle('') @@ -85,10 +95,31 @@ export function SnippetDialog({ open, onOpenChange, onSave, editingSnippet }: Sn setLanguage(appConfig.defaultLanguage) setCode('') setHasPreview(false) + setFunctionName('') + setInputParameters([]) setErrors({}) onOpenChange(false) } + const handleAddParameter = () => { + setInputParameters((prev) => [ + ...prev, + { name: '', type: 'string', defaultValue: '', description: '' } + ]) + } + + const handleRemoveParameter = (index: number) => { + setInputParameters((prev) => prev.filter((_, i) => i !== index)) + } + + const handleUpdateParameter = (index: number, field: keyof InputParameter, value: string) => { + setInputParameters((prev) => + prev.map((param, i) => + i === index ? { ...param, [field]: value } : param + ) + ) + } + return ( @@ -152,6 +183,140 @@ export function SnippetDialog({ open, onOpenChange, onSave, editingSnippet }: Sn )} + {hasPreview && appConfig.previewEnabledLanguages.includes(language) && ( + + + + Preview Configuration + + + + +
+ + setFunctionName(e.target.value)} + className="bg-background" + /> +

+ The name of the function or component to render. Leave empty to use the default export. +

+
+ + {inputParameters.length > 0 && ( +
+ + {inputParameters.map((param, index) => ( + + +
+
+
+ + + handleUpdateParameter(index, 'name', e.target.value) + } + className="h-8 text-sm" + /> +
+
+ + +
+
+ +
+
+ + + handleUpdateParameter(index, 'defaultValue', e.target.value) + } + className="h-8 text-sm font-mono" + /> +
+
+ + + handleUpdateParameter(index, 'description', e.target.value) + } + className="h-8 text-sm" + /> +
+
+
+ ))} +
+ )} +
+
+ )} +