ImageView.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import React, { useState } from "react";
  2. import { generateImage } from "../services/gemini";
  3. import { GeneratedImage } from "../services/types";
  4. const ImageView: React.FC = () => {
  5. const [prompt, setPrompt] = useState("");
  6. const [loading, setLoading] = useState(false);
  7. const [history, setHistory] = useState<GeneratedImage[]>([]);
  8. const [aspectRatio, setAspectRatio] = useState<"1:1" | "16:9" | "9:16">(
  9. "1:1"
  10. );
  11. const handleGenerate = async () => {
  12. if (!prompt.trim() || loading) return;
  13. setLoading(true);
  14. try {
  15. const url = await generateImage(prompt, aspectRatio);
  16. setHistory((prev) => [{ url, prompt, timestamp: new Date() }, ...prev]);
  17. } catch (error) {
  18. console.error(error);
  19. alert("Asset generation failed. Please refine your parameters.");
  20. } finally {
  21. setLoading(false);
  22. }
  23. };
  24. return (
  25. <div className="flex flex-col h-full bg-slate-50">
  26. <div className="p-10 bg-white border-b border-slate-200 shadow-sm z-10">
  27. <div className="max-w-7xl mx-auto flex flex-col space-y-6">
  28. <div>
  29. <h2 className="text-3xl font-black text-slate-900 tracking-tight">
  30. Image Forge
  31. </h2>
  32. <p className="text-slate-500 font-medium">
  33. Generate high-fidelity visual assets for your brand and projects.
  34. </p>
  35. </div>
  36. <div className="flex flex-col lg:flex-row gap-4">
  37. <div className="flex-1 relative group">
  38. <input
  39. type="text"
  40. value={prompt}
  41. onChange={(e) => setPrompt(e.target.value)}
  42. placeholder="High-resolution architectural render of a modern workspace..."
  43. className="w-full bg-slate-50 text-slate-800 rounded-2xl px-6 py-4 focus:outline-none focus:ring-4 focus:ring-blue-500/10 border border-slate-200 text-lg transition-all"
  44. />
  45. </div>
  46. <div className="flex gap-4">
  47. <select
  48. value={aspectRatio}
  49. onChange={(e: any) => setAspectRatio(e.target.value)}
  50. className="bg-white text-slate-700 border border-slate-200 rounded-2xl px-6 py-4 outline-none focus:ring-2 focus:ring-[#004ea8] font-semibold text-sm shadow-sm cursor-pointer hover:bg-slate-50 transition-colors"
  51. >
  52. <option value="1:1">Standard (1:1)</option>
  53. <option value="16:9">Widescreen (16:9)</option>
  54. <option value="9:16">Mobile (9:16)</option>
  55. </select>
  56. <button
  57. onClick={handleGenerate}
  58. disabled={loading}
  59. className="bg-[#004ea8] hover:bg-[#003d82] disabled:opacity-50 text-white rounded-2xl px-10 py-4 font-bold transition-all shadow-xl active:scale-95 whitespace-nowrap"
  60. >
  61. {loading ? "Synthesizing..." : "Generate Assets"}
  62. </button>
  63. </div>
  64. </div>
  65. </div>
  66. </div>
  67. <div className="flex-1 overflow-y-auto p-10">
  68. <div className="max-w-7xl mx-auto">
  69. {history.length === 0 && !loading && (
  70. <div className="h-[50vh] flex flex-col items-center justify-center opacity-40">
  71. <div className="p-10 bg-white rounded-[40px] shadow-2xl border border-slate-100 mb-8">
  72. <svg
  73. className="w-24 h-24 text-slate-200"
  74. fill="none"
  75. stroke="currentColor"
  76. viewBox="0 0 24 24"
  77. >
  78. <path
  79. strokeLinecap="round"
  80. strokeLinejoin="round"
  81. strokeWidth={1}
  82. d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
  83. />
  84. </svg>
  85. </div>
  86. <p className="text-2xl font-bold text-slate-400">
  87. Project Gallery Empty
  88. </p>
  89. <p className="text-slate-300 mt-2">
  90. Initialize generation to view results.
  91. </p>
  92. </div>
  93. )}
  94. <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10">
  95. {loading && (
  96. <div className="aspect-square bg-white border border-slate-200 rounded-[32px] flex flex-col items-center justify-center shadow-xl animate-pulse">
  97. <div className="w-16 h-16 border-4 border-[#004ea8] border-t-transparent rounded-full animate-spin"></div>
  98. <p className="mt-6 text-slate-500 font-bold uppercase tracking-widest text-xs">
  99. Computing Vision...
  100. </p>
  101. </div>
  102. )}
  103. {history.map((img, i) => (
  104. <div
  105. key={i}
  106. className="group relative bg-white border border-slate-200 rounded-[32px] overflow-hidden shadow-lg hover:shadow-2xl transition-all hover:scale-[1.03]"
  107. >
  108. <img
  109. src={img.url}
  110. alt={img.prompt}
  111. className="w-full h-auto object-cover"
  112. />
  113. <div className="absolute inset-0 bg-gradient-to-t from-[#004ea8]/90 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-all p-8 flex flex-col justify-end">
  114. <div className="bg-white/10 backdrop-blur-xl p-4 rounded-2xl border border-white/20">
  115. <p className="text-sm text-white font-semibold line-clamp-2">
  116. "{img.prompt}"
  117. </p>
  118. <a
  119. href={img.url}
  120. download={`asset-${i}.png`}
  121. className="mt-4 block w-full bg-white text-[#004ea8] text-center py-3 rounded-xl text-xs font-black uppercase tracking-widest transition-colors hover:bg-slate-100"
  122. >
  123. Export Resolution
  124. </a>
  125. </div>
  126. </div>
  127. </div>
  128. ))}
  129. </div>
  130. </div>
  131. </div>
  132. </div>
  133. );
  134. };
  135. export default ImageView;