From b409f168d15154a59d89715c92f924b5cc2597ee Mon Sep 17 00:00:00 2001
From: johndoe6345789
Date: Sat, 17 Jan 2026 14:43:49 +0000
Subject: [PATCH] Generated by Spark: Have option to have a split screen
snippet. Code + Preview. This could allow rendering a React app.
---
PRD.md | 42 ++++++++----
spark.meta.json | 6 +-
src/components/ReactPreview.tsx | 107 +++++++++++++++++++++++++++++++
src/components/SnippetCard.tsx | 29 ++++++---
src/components/SnippetDialog.tsx | 22 +++++++
src/components/SnippetViewer.tsx | 52 ++++++++++++---
src/lib/types.ts | 5 ++
7 files changed, 230 insertions(+), 33 deletions(-)
create mode 100644 src/components/ReactPreview.tsx
diff --git a/PRD.md b/PRD.md
index 831ac02..9793fb1 100644
--- a/PRD.md
+++ b/PRD.md
@@ -13,18 +13,25 @@ This is a CRUD application with search, filtering, and organization features but
## Essential Features
### Create Snippet
-- **Functionality**: Users can create a new code snippet with title, description, language selection, and code content using Monaco Editor for enhanced code editing
-- **Purpose**: Core value proposition - storing reusable code for later retrieval with professional IDE-like experience
+- **Functionality**: Users can create a new code snippet with title, description, language selection, code content using Monaco Editor, and optional preview mode for React components
+- **Purpose**: Core value proposition - storing reusable code for later retrieval with professional IDE-like experience and live preview capability for React snippets
- **Trigger**: Click "New Snippet" button or keyboard shortcut
-- **Progression**: Click New Snippet → Fill in title field → Select language from dropdown → Write/paste code in Monaco Editor with syntax highlighting → Add optional description → Click Save
-- **Success criteria**: Snippet appears in the list immediately, persists across page refreshes, Monaco Editor loads lazily without blocking UI, and code is searchable
+- **Progression**: Click New Snippet → Fill in title field → Select language from dropdown (including JSX/TSX options) → Toggle preview mode checkbox if creating React component → Write/paste code in Monaco Editor with syntax highlighting → Add optional description → Click Save
+- **Success criteria**: Snippet appears in the list immediately with preview badge if enabled, persists across page refreshes, Monaco Editor loads lazily without blocking UI, and code is searchable
### View & Organize Snippets
-- **Functionality**: Display all snippets in a filterable list with preview cards showing title, language, and truncated code; click to open full-screen Monaco viewer
-- **Purpose**: Quick scanning and navigation through saved snippets with professional code viewing
+- **Functionality**: Display all snippets in a filterable list with preview cards showing title, language, preview badge, and truncated code; click to open full-screen Monaco viewer with optional split-screen preview
+- **Purpose**: Quick scanning and navigation through saved snippets with professional code viewing and live React component preview
- **Trigger**: Default view on app load, click card to view full code
-- **Progression**: View list → Scan titles and languages → Click card to open full-screen Monaco viewer with syntax highlighting → Copy or edit from viewer
-- **Success criteria**: All snippets visible, sorted by recent first, viewer opens instantly with lazy-loaded Monaco Editor
+- **Progression**: View list → Scan titles, languages, and preview badges → Click card to open full-screen viewer with syntax highlighting → Toggle split-screen preview for React components → Copy or edit from viewer
+- **Success criteria**: All snippets visible, sorted by recent first, viewer opens instantly with lazy-loaded Monaco Editor, preview renders React components in real-time
+
+### Split-Screen Preview
+- **Functionality**: For React-compatible snippets (JSX/TSX/JavaScript/TypeScript), render live preview alongside code editor in split-screen layout
+- **Purpose**: Enable developers to see React components rendered in real-time, test UI snippets instantly, and save complete working component examples
+- **Trigger**: Enable preview mode checkbox when creating/editing snippet, toggle preview button in viewer
+- **Progression**: Create snippet with JSX/TSX → Check "Enable preview" → Save → Open viewer → See code on left, live preview on right → Toggle preview on/off as needed
+- **Success criteria**: React code compiles and renders safely, errors display helpful messages, preview updates reflect code changes, supports React hooks and JSX syntax
### Search & Filter
- **Functionality**: Real-time search across snippet titles, descriptions, and code content; filter by programming language
@@ -54,6 +61,8 @@ 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 Not Available**: Show informative message for snippets without preview enabled or non-React languages
## 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.
@@ -91,20 +100,25 @@ Animations should feel responsive and technical - quick, purposeful movements th
- Button for all actions (primary solid style for Save/Create, ghost style for secondary actions)
- Select for language dropdown with custom styling to match theme
- Textarea for code input with monospace font
- - Badge for language tags with color coding
+ - Badge for language tags with color coding, preview indicator badge with split-screen icon
- ScrollArea for long code blocks within cards
- Toast (Sonner) for copy confirmations and success messages
- AlertDialog for delete confirmations
+ - Checkbox for enabling split-screen preview mode
+ - Alert for displaying preview rendering errors
- **Customizations**:
- - Custom syntax language badges with predefined color mapping (JavaScript=yellow, Python=blue, etc.)
+ - Custom syntax language badges with predefined color mapping (JavaScript=yellow, Python=blue, JSX/TSX=cyan/sky, etc.)
- Floating action button for "New Snippet" with plus icon, fixed bottom-right on mobile
- Custom empty state component with illustration or icon and encouraging copy
+ - Split-screen preview renderer with safe React code execution
+ - Preview badge with split-screen icon to indicate preview-enabled snippets
- **States**:
- - Buttons: Default with subtle gradient, hover with brightness increase and shadow, active with slight scale-down (0.98), disabled with 50% opacity
- - Cards: Default flat, hover with shadow-lg and border glow, selected/expanded with accent border
+ - Buttons: Default with subtle gradient, hover with brightness increase and shadow, active with slight scale-down (0.98), disabled with 50% opacity, toggle state for preview on/off
+ - Cards: Default flat, hover with shadow-lg and border glow, selected/expanded with accent border, preview badge visible when enabled
- Inputs: Default with muted border, focus with accent ring and border color shift, error with red ring
+ - Preview pane: Loading state, error state with helpful message, rendered state with scrolling
- **Icon Selection**:
- Plus (create new snippet)
@@ -114,6 +128,8 @@ Animations should feel responsive and technical - quick, purposeful movements th
- MagnifyingGlass (search)
- Code (app logo/branding)
- Check (confirmation feedback)
+ - SplitVertical (preview mode indicator and toggle)
+ - WarningCircle (preview errors)
- **Spacing**:
- Container padding: p-6 (desktop) / p-4 (mobile)
@@ -121,6 +137,7 @@ Animations should feel responsive and technical - quick, purposeful movements th
- Form fields: space-y-4 for vertical stacking
- Section margins: mb-8 for major sections
- Inline elements: gap-2 for icon+text combinations
+ - Split-screen: Equal 50/50 width distribution with border separator
- **Mobile**:
- Single column card layout with full-width cards
@@ -129,3 +146,4 @@ Animations should feel responsive and technical - quick, purposeful movements th
- Floating action button (FAB) for create action instead of header button
- Touch-friendly button sizes (min 44px tap targets)
- Bottom sheet for language filter on mobile vs. dropdown on desktop
+ - Preview stacks vertically below code editor on small screens
diff --git a/spark.meta.json b/spark.meta.json
index f0cd233..fd74d91 100644
--- a/spark.meta.json
+++ b/spark.meta.json
@@ -1,4 +1,4 @@
-{
- "dbType": null
- "dbType": null
+{
+ "templateVersion": 0,
+ "dbType": null
}
\ No newline at end of file
diff --git a/src/components/ReactPreview.tsx b/src/components/ReactPreview.tsx
new file mode 100644
index 0000000..2c4d1cd
--- /dev/null
+++ b/src/components/ReactPreview.tsx
@@ -0,0 +1,107 @@
+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 { WarningCircle } from '@phosphor-icons/react'
+
+interface ReactPreviewProps {
+ code: string
+ language: string
+}
+
+export function ReactPreview({ code, language }: ReactPreviewProps) {
+ const [error, setError] = useState(null)
+ const [Component, setComponent] = useState(null)
+ const mountRef = useRef(null)
+
+ useEffect(() => {
+ setError(null)
+ setComponent(null)
+
+ const isReactCode = ['JSX', 'TSX', 'JavaScript', 'TypeScript'].includes(language)
+
+ if (!isReactCode) {
+ return
+ }
+
+ try {
+ const transformedCode = code
+ .replace(/^import\s+.*from\s+['"]react['"];?\s*/gm, '')
+ .replace(/^import\s+.*from\s+['"].*['"];?\s*/gm, '')
+ .replace(/export\s+default\s+/g, '')
+ .replace(/export\s+/g, '')
+
+ const wrappedCode = `
+ (function() {
+ const React = arguments[0];
+ const useState = React.useState;
+ const useEffect = React.useEffect;
+ const useRef = React.useRef;
+ const useMemo = React.useMemo;
+ const useCallback = React.useCallback;
+
+ ${transformedCode}
+
+ const lastStatement = (${transformedCode.trim().split('\n').pop()});
+ return lastStatement;
+ })
+ `
+
+ const componentFactory = eval(wrappedCode)
+ const CreatedComponent = componentFactory(React)
+
+ if (typeof CreatedComponent === 'function') {
+ setComponent(() => CreatedComponent)
+ } else if (React.isValidElement(CreatedComponent)) {
+ setComponent(() => () => CreatedComponent)
+ } else {
+ setError('Code must export a React component or JSX element')
+ }
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to render preview')
+ }
+ }, [code, language])
+
+ if (!['JSX', 'TSX', 'JavaScript', 'TypeScript'].includes(language)) {
+ return (
+
+
+
+
Preview not available for {language}
+
Use JSX, TSX, JavaScript, or TypeScript
+
+
+ )
+ }
+
+ if (error) {
+ return (
+
+
+
+
+ {error}
+
+
+
+ )
+ }
+
+ if (!Component) {
+ return (
+
+
+
Loading preview...
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ )
+}
diff --git a/src/components/SnippetCard.tsx b/src/components/SnippetCard.tsx
index dd73d99..1df6439 100644
--- a/src/components/SnippetCard.tsx
+++ b/src/components/SnippetCard.tsx
@@ -2,7 +2,7 @@ import { useState } from 'react'
import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
-import { Copy, Pencil, Trash, Check, ArrowsOut } from '@phosphor-icons/react'
+import { Copy, Pencil, Trash, Check, ArrowsOut, SplitVertical } from '@phosphor-icons/react'
import { Snippet, LANGUAGE_COLORS } from '@/lib/types'
import { cn } from '@/lib/utils'
@@ -46,15 +46,26 @@ export function SnippetCard({ snippet, onEdit, onDelete, onCopy, onView }: Snipp