Generated by Spark: Add preview button to toolbar

This commit is contained in:
2026-01-17 00:12:23 +00:00
committed by GitHub
parent 128d2ae6f4
commit ad39354a8e
4 changed files with 89 additions and 0 deletions

View File

@@ -37,6 +37,7 @@ const componentMap: Record<string, React.LazyExoticComponent<any>> = {
const GlobalSearch = lazy(() => import('@/components/GlobalSearch').then(m => ({ default: m.GlobalSearch })))
const KeyboardShortcutsDialog = lazy(() => import('@/components/KeyboardShortcutsDialog').then(m => ({ default: m.KeyboardShortcutsDialog })))
const PreviewDialog = lazy(() => import('@/components/PreviewDialog').then(m => ({ default: m.PreviewDialog })))
const PWAInstallPrompt = lazy(() => import('@/components/PWAInstallPrompt').then(m => ({ default: m.PWAInstallPrompt })))
const PWAUpdatePrompt = lazy(() => import('@/components/PWAUpdatePrompt').then(m => ({ default: m.PWAUpdatePrompt })))
const PWAStatusBar = lazy(() => import('@/components/PWAStatusBar').then(m => ({ default: m.PWAStatusBar })))
@@ -81,6 +82,7 @@ function App() {
const [activeTab, setActiveTab] = useState('dashboard')
const [searchOpen, setSearchOpen] = useState(false)
const [shortcutsOpen, setShortcutsOpen] = useState(false)
const [previewOpen, setPreviewOpen] = useState(false)
const [lastSaved] = useState<number | null>(Date.now())
const [errorCount] = useState(0)
const [appReady, setAppReady] = useState(false)
@@ -116,6 +118,7 @@ function App() {
})),
{ key: 'k', ctrl: true, description: 'Search', action: () => setSearchOpen(true) },
{ key: '/', ctrl: true, description: 'Shortcuts', action: () => setShortcutsOpen(true) },
{ key: 'p', ctrl: true, description: 'Preview', action: () => setPreviewOpen(true) },
])
const getCurrentProject = () => ({
@@ -324,6 +327,7 @@ function App() {
onShowShortcuts={() => setShortcutsOpen(true)}
onGenerateAI={() => toast.info('AI generation coming soon')}
onExport={() => toast.info('Export coming soon')}
onPreview={() => setPreviewOpen(true)}
onShowErrors={() => setActiveTab('errors')}
/>
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col">
@@ -358,6 +362,9 @@ function App() {
<Suspense fallback={null}>
<KeyboardShortcutsDialog open={shortcutsOpen} onOpenChange={setShortcutsOpen} />
</Suspense>
<Suspense fallback={null}>
<PreviewDialog open={previewOpen} onOpenChange={setPreviewOpen} />
</Suspense>
<Suspense fallback={null}>
<PWAInstallPrompt />
</Suspense>

View File

@@ -0,0 +1,67 @@
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { X, ArrowSquareOut } from '@phosphor-icons/react'
import { useEffect, useState } from 'react'
interface PreviewDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
}
export function PreviewDialog({ open, onOpenChange }: PreviewDialogProps) {
const [previewUrl, setPreviewUrl] = useState<string>('')
useEffect(() => {
if (open) {
const currentUrl = window.location.href
const url = new URL(currentUrl)
url.searchParams.set('preview', 'true')
setPreviewUrl(url.toString())
}
}, [open])
const handleOpenInNewTab = () => {
if (previewUrl) {
window.open(previewUrl, '_blank', 'noopener,noreferrer')
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-[95vw] w-[95vw] h-[90vh] p-0 gap-0">
<DialogHeader className="px-6 py-4 border-b border-border flex flex-row items-center justify-between space-y-0">
<DialogTitle className="text-lg font-semibold">Preview</DialogTitle>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={handleOpenInNewTab}
className="gap-2"
>
<ArrowSquareOut size={16} />
Open in New Tab
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => onOpenChange(false)}
className="h-8 w-8 p-0"
>
<X size={20} />
</Button>
</div>
</DialogHeader>
<div className="flex-1 w-full h-full overflow-hidden">
{previewUrl && (
<iframe
src={previewUrl}
className="w-full h-full border-0"
title="Preview"
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals"
/>
)}
</div>
</DialogContent>
</Dialog>
)
}

View File

@@ -16,6 +16,7 @@ interface AppHeaderProps {
onShowShortcuts: () => void
onGenerateAI: () => void
onExport: () => void
onPreview?: () => void
onShowErrors: () => void
}
@@ -31,6 +32,7 @@ export function AppHeader({
onShowShortcuts,
onGenerateAI,
onExport,
onPreview,
onShowErrors,
}: AppHeaderProps) {
return (
@@ -56,6 +58,7 @@ export function AppHeader({
onShowShortcuts={onShowShortcuts}
onGenerateAI={onGenerateAI}
onExport={onExport}
onPreview={onPreview}
onShowErrors={onShowErrors}
errorCount={errorCount}
showErrorButton={featureToggles.errorRepair && errorCount > 0}

View File

@@ -6,6 +6,7 @@ import {
Sparkle,
Download,
Wrench,
Eye,
} from '@phosphor-icons/react'
interface ToolbarActionsProps {
@@ -13,6 +14,7 @@ interface ToolbarActionsProps {
onShowShortcuts: () => void
onGenerateAI: () => void
onExport: () => void
onPreview?: () => void
onShowErrors?: () => void
errorCount?: number
showErrorButton?: boolean
@@ -23,6 +25,7 @@ export function ToolbarActions({
onShowShortcuts,
onGenerateAI,
onExport,
onPreview,
onShowErrors,
errorCount = 0,
showErrorButton = false,
@@ -48,6 +51,15 @@ export function ToolbarActions({
</div>
)}
{onPreview && (
<ToolbarButton
icon={<Eye size={18} />}
label="Preview (Ctrl+P)"
onClick={onPreview}
variant="outline"
/>
)}
<ToolbarButton
icon={<Keyboard size={18} />}
label="Keyboard Shortcuts (Ctrl+/)"