Spaces:
Running
Running
Merge pull request #3 from novitalabs/feat/interaction-optimization
Browse files
src/components/preview.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
import { useState, forwardRef, useImperativeHandle, useEffect, useRef } from "react"
|
| 4 |
import { DEFAULT_HTML } from "@/lib/constants"
|
| 5 |
import { PreviewRef, Version } from "@/lib/types"
|
| 6 |
-
import { MinimizeIcon, MaximizeIcon, DownloadIcon } from "./ui/
|
| 7 |
import { useModel } from "@/lib/contexts/model-context"
|
| 8 |
import { Loader2 } from "lucide-react"
|
| 9 |
import { cn } from "@/lib/utils"
|
|
@@ -27,6 +27,7 @@ export const Preview = forwardRef<PreviewRef, PreviewProps>(function Preview(
|
|
| 27 |
const [isPartialGenerating, setIsPartialGenerating] = useState(false);
|
| 28 |
const [error, setError] = useState<string | null>(null);
|
| 29 |
const [showAuthError, setShowAuthError] = useState(false);
|
|
|
|
| 30 |
const { selectedModelId } = useModel();
|
| 31 |
const renderCount = useRef(0);
|
| 32 |
const headUpdated = useRef(false);
|
|
@@ -103,6 +104,11 @@ export const Preview = forwardRef<PreviewRef, PreviewProps>(function Preview(
|
|
| 103 |
document.body.removeChild(a);
|
| 104 |
};
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
const generateCode = async (prompt: string, colors: string[] = [], previousPrompt?: string) => {
|
| 107 |
setLoading(true);
|
| 108 |
renderCount.current = 0;
|
|
@@ -265,6 +271,14 @@ export const Preview = forwardRef<PreviewRef, PreviewProps>(function Preview(
|
|
| 265 |
)}
|
| 266 |
<div className="bg-white text-black h-full overflow-hidden relative isolation-auto">
|
| 267 |
<div className="absolute top-3 right-3 flex gap-2 z-[100]">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
<button
|
| 269 |
onClick={downloadHtml}
|
| 270 |
className="bg-novita-gray/90 text-white p-2 rounded-md shadow-md hover:bg-novita-gray/70 transition-colors flex items-center justify-center"
|
|
@@ -283,6 +297,7 @@ export const Preview = forwardRef<PreviewRef, PreviewProps>(function Preview(
|
|
| 283 |
</button>
|
| 284 |
</div>
|
| 285 |
<iframe
|
|
|
|
| 286 |
className={cn("relative z-10 w-full h-full select-none", {
|
| 287 |
"pointer-events-none": loading,
|
| 288 |
})}
|
|
|
|
| 3 |
import { useState, forwardRef, useImperativeHandle, useEffect, useRef } from "react"
|
| 4 |
import { DEFAULT_HTML } from "@/lib/constants"
|
| 5 |
import { PreviewRef, Version } from "@/lib/types"
|
| 6 |
+
import { MinimizeIcon, MaximizeIcon, DownloadIcon, RefreshIcon } from "./ui/icons"
|
| 7 |
import { useModel } from "@/lib/contexts/model-context"
|
| 8 |
import { Loader2 } from "lucide-react"
|
| 9 |
import { cn } from "@/lib/utils"
|
|
|
|
| 27 |
const [isPartialGenerating, setIsPartialGenerating] = useState(false);
|
| 28 |
const [error, setError] = useState<string | null>(null);
|
| 29 |
const [showAuthError, setShowAuthError] = useState(false);
|
| 30 |
+
const [refreshKey, setRefreshKey] = useState(0);
|
| 31 |
const { selectedModelId } = useModel();
|
| 32 |
const renderCount = useRef(0);
|
| 33 |
const headUpdated = useRef(false);
|
|
|
|
| 104 |
document.body.removeChild(a);
|
| 105 |
};
|
| 106 |
|
| 107 |
+
const refreshPreview = () => {
|
| 108 |
+
if (!html) return;
|
| 109 |
+
setRefreshKey(prev => prev + 1);
|
| 110 |
+
};
|
| 111 |
+
|
| 112 |
const generateCode = async (prompt: string, colors: string[] = [], previousPrompt?: string) => {
|
| 113 |
setLoading(true);
|
| 114 |
renderCount.current = 0;
|
|
|
|
| 271 |
)}
|
| 272 |
<div className="bg-white text-black h-full overflow-hidden relative isolation-auto">
|
| 273 |
<div className="absolute top-3 right-3 flex gap-2 z-[100]">
|
| 274 |
+
<button
|
| 275 |
+
onClick={refreshPreview}
|
| 276 |
+
className="bg-novita-gray/90 text-white p-2 rounded-md shadow-md hover:bg-novita-gray/70 transition-colors flex items-center justify-center"
|
| 277 |
+
aria-label="Refresh Preview"
|
| 278 |
+
title="Refresh Preview"
|
| 279 |
+
>
|
| 280 |
+
<RefreshIcon />
|
| 281 |
+
</button>
|
| 282 |
<button
|
| 283 |
onClick={downloadHtml}
|
| 284 |
className="bg-novita-gray/90 text-white p-2 rounded-md shadow-md hover:bg-novita-gray/70 transition-colors flex items-center justify-center"
|
|
|
|
| 297 |
</button>
|
| 298 |
</div>
|
| 299 |
<iframe
|
| 300 |
+
key={refreshKey}
|
| 301 |
className={cn("relative z-10 w-full h-full select-none", {
|
| 302 |
"pointer-events-none": loading,
|
| 303 |
})}
|
src/components/ui/{fullscreen-icons.tsx → icons.tsx}
RENAMED
|
@@ -28,4 +28,15 @@ export function DownloadIcon() {
|
|
| 28 |
<line x1="12" y1="15" x2="12" y2="3"></line>
|
| 29 |
</svg>
|
| 30 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
}
|
|
|
|
| 28 |
<line x1="12" y1="15" x2="12" y2="3"></line>
|
| 29 |
</svg>
|
| 30 |
)
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
export function RefreshIcon() {
|
| 34 |
+
return (
|
| 35 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
| 36 |
+
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path>
|
| 37 |
+
<path d="M21 3v5h-5"></path>
|
| 38 |
+
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path>
|
| 39 |
+
<path d="M3 21v-5h5"></path>
|
| 40 |
+
</svg>
|
| 41 |
+
)
|
| 42 |
}
|