) => {
+ if (!flaskUrl) {
+ toast.error('Please enter a Flask backend URL')
+ return
+ }
+
+ try {
+ const adapter = new FlaskStorageAdapter(flaskUrl)
+ const connected = await adapter.testConnection()
+
+ if (!connected) {
+ toast.error('Cannot connect to Flask backend')
+ return
+ }
+
+ const snippets = await getAllSnippets()
+
+ if (snippets.length === 0) {
+ toast.info('No snippets to migrate')
+ return
+ }
+
+ await adapter.migrateFromIndexedDB(snippets)
+
+ saveStorageConfig({
+ backend: 'flask',
+ flaskUrl
+ })
+
+ toast.success(`Successfully migrated ${snippets.length} snippets to Flask backend`)
+
+ if (onSuccess) {
+ await onSuccess()
+ }
+ } catch (error) {
+ console.error('Migration failed:', error)
+ toast.error('Failed to migrate data to Flask backend')
+ }
+ }, [])
+
+ const handleMigrateToIndexedDB = useCallback(async (flaskUrl: string) => {
+ if (!flaskUrl) {
+ toast.error('Please enter a Flask backend URL')
+ return
+ }
+
+ try {
+ const adapter = new FlaskStorageAdapter(flaskUrl)
+ const snippets = await adapter.migrateToIndexedDB()
+
+ if (snippets.length === 0) {
+ toast.info('No snippets to migrate')
+ return
+ }
+
+ saveStorageConfig({
+ backend: 'indexeddb'
+ })
+
+ // Full page reload is necessary here to reinitialize the database layer
+ // with the new backend after migration from Flask to IndexedDB
+ window.location.reload()
+
+ toast.success(`Successfully migrated ${snippets.length} snippets to IndexedDB`)
+ } catch (error) {
+ console.error('Migration failed:', error)
+ toast.error('Failed to migrate data from Flask backend')
+ }
+ }, [])
+
+ return {
+ handleMigrateToFlask,
+ handleMigrateToIndexedDB,
+ }
+}
diff --git a/src/pages/DemoFeatureCards.tsx b/src/pages/DemoFeatureCards.tsx
new file mode 100644
index 0000000..9b3652e
--- /dev/null
+++ b/src/pages/DemoFeatureCards.tsx
@@ -0,0 +1,34 @@
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+
+export function DemoFeatureCards() {
+ return (
+
+
+
+ Real-Time Updates
+
+
+ Watch your React components render instantly as you type. No refresh needed.
+
+
+
+
+
+ Resizable Panels
+
+
+ Drag the center divider to adjust the editor and preview panel sizes to your preference.
+
+
+
+
+
+ Multiple View Modes
+
+
+ Switch between code-only, split-screen, or preview-only modes with the toggle buttons.
+
+
+
+ )
+}
diff --git a/src/pages/DemoPage.tsx b/src/pages/DemoPage.tsx
index 749655e..ce0a9da 100644
--- a/src/pages/DemoPage.tsx
+++ b/src/pages/DemoPage.tsx
@@ -3,117 +3,8 @@ import { motion } from 'framer-motion'
import { SplitScreenEditor } from '@/components/features/snippet-editor/SplitScreenEditor'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Sparkle } from '@phosphor-icons/react'
-
-const DEMO_CODE = `function Counter() {
- const [count, setCount] = React.useState(0)
-
- return (
-
-
- Interactive Counter
-
-
-
- {count}
-
-
-
- setCount(count - 1)}
- style={{
- padding: '0.75rem 2rem',
- fontSize: '1.125rem',
- fontWeight: '600',
- background: '#ef4444',
- color: 'white',
- border: 'none',
- borderRadius: '0.5rem',
- cursor: 'pointer',
- transition: 'all 0.2s',
- boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
- }}
- onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.05)'}
- onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
- >
- Decrement
-
-
- setCount(0)}
- style={{
- padding: '0.75rem 2rem',
- fontSize: '1.125rem',
- fontWeight: '600',
- background: '#6b7280',
- color: 'white',
- border: 'none',
- borderRadius: '0.5rem',
- cursor: 'pointer',
- transition: 'all 0.2s',
- boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
- }}
- onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.05)'}
- onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
- >
- Reset
-
-
- setCount(count + 1)}
- style={{
- padding: '0.75rem 2rem',
- fontSize: '1.125rem',
- fontWeight: '600',
- background: '#10b981',
- color: 'white',
- border: 'none',
- borderRadius: '0.5rem',
- cursor: 'pointer',
- transition: 'all 0.2s',
- boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
- }}
- onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.05)'}
- onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
- >
- Increment
-
-
-
-
- Try editing the code on the left to see live changes!
-
-
- )
-}
-
-Counter`
+import { DEMO_CODE } from './demo-constants'
+import { DemoFeatureCards } from './DemoFeatureCards'
export function DemoPage() {
const [code, setCode] = useState(DEMO_CODE)
@@ -158,34 +49,7 @@ export function DemoPage() {
-
-
-
- Real-Time Updates
-
-
- Watch your React components render instantly as you type. No refresh needed.
-
-
-
-
-
- Resizable Panels
-
-
- Drag the center divider to adjust the editor and preview panel sizes to your preference.
-
-
-
-
-
- Multiple View Modes
-
-
- Switch between code-only, split-screen, or preview-only modes with the toggle buttons.
-
-
-
+
)
}
diff --git a/src/pages/demo-constants.ts b/src/pages/demo-constants.ts
new file mode 100644
index 0000000..8abcefe
--- /dev/null
+++ b/src/pages/demo-constants.ts
@@ -0,0 +1,110 @@
+export const DEMO_CODE = `function Counter() {
+ const [count, setCount] = React.useState(0)
+
+ return (
+
+
+ Interactive Counter
+
+
+
+ {count}
+
+
+
+ setCount(count - 1)}
+ style={{
+ padding: '0.75rem 2rem',
+ fontSize: '1.125rem',
+ fontWeight: '600',
+ background: '#ef4444',
+ color: 'white',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ transition: 'all 0.2s',
+ boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
+ }}
+ onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.05)'}
+ onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
+ >
+ Decrement
+
+
+ setCount(0)}
+ style={{
+ padding: '0.75rem 2rem',
+ fontSize: '1.125rem',
+ fontWeight: '600',
+ background: '#6b7280',
+ color: 'white',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ transition: 'all 0.2s',
+ boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
+ }}
+ onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.05)'}
+ onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
+ >
+ Reset
+
+
+ setCount(count + 1)}
+ style={{
+ padding: '0.75rem 2rem',
+ fontSize: '1.125rem',
+ fontWeight: '600',
+ background: '#10b981',
+ color: 'white',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ transition: 'all 0.2s',
+ boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
+ }}
+ onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.05)'}
+ onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
+ >
+ Increment
+
+
+
+
+ Try editing the code on the left to see live changes!
+
+
+ )
+}
+
+Counter`