Akimitsujiro commited on
Commit
3f6fdde
·
verified ·
1 Parent(s): 0fba08d

Update src/App.jsx

Browse files
Files changed (1) hide show
  1. src/App.jsx +47 -41
src/App.jsx CHANGED
@@ -1,20 +1,17 @@
1
  import React, { useState } from 'react';
2
  import {
3
  Sparkles, Copy, Trash2, Wand2, Terminal,
4
- Palette, Zap, Check, FlaskConical, Skull, Heart, BrainCircuit, Loader2, AlertCircle
5
  } from 'lucide-react';
6
- import { HfInference } from "@huggingface/inference";
 
7
 
8
- // --- CẤU HÌNH HUGGING FACE ---
9
- // Lưu ý: Để dùng ổn định, bạn nên tạo HF Token (Read) dán vào đây hoặc dùng biến môi trường
10
- // Nếu để trống, nó sẽ dùng chế độ public (có thể bị rate limit)
11
- const HF_TOKEN = "";
12
- const hf = new HfInference(HF_TOKEN);
13
 
14
- // Model chuyên chat/instruct tốt
15
  const AI_MODEL = "HuggingFaceH4/zephyr-7b-beta";
16
 
17
- // --- DATABASE TỪ KHÓA (Cho chế độ Manual) ---
18
  const STYLES = [
19
  { id: 'none', label: 'None', prompt: '' },
20
  { id: 'anime', label: 'Anime/Manga', prompt: 'anime style, studio ghibli style, vibrant colors, makoto shinkai style, detailed background' },
@@ -43,8 +40,9 @@ const App = () => {
43
  const [finalPrompt, setFinalPrompt] = useState("");
44
  const [copied, setCopied] = useState(false);
45
 
46
- // State mới cho chế độ AI
47
  const [useAI, setUseAI] = useState(false);
 
48
  const [isLoadingAI, setIsLoadingAI] = useState(false);
49
  const [aiError, setAiError] = useState("");
50
 
@@ -64,16 +62,21 @@ const App = () => {
64
  const generateWithAI = async () => {
65
  if (!basePrompt.trim()) return;
66
 
 
 
 
 
67
  setIsLoadingAI(true);
68
  setAiError("");
69
- setFinalPrompt(""); // Clear cũ
70
 
71
  try {
 
 
72
  const styleText = selectedStyle.id !== 'none' ? `Style: ${selectedStyle.label} (${selectedStyle.prompt}).` : "";
73
  const qualityText = addQuality ? "Ensure high quality, detailed, 8k resolution." : "";
74
  const customText = customTags ? `Include these elements: ${customTags}.` : "";
75
 
76
- // Prompt kỹ thuật (System Prompt) để ép AI trả về format chuẩn SD
77
  const systemPrompt = `You are an expert Stable Diffusion prompt engineer.
78
  Task: Rewrite the user's concept into a highly detailed, comma-separated prompt optimized for SDXL.
79
  Rules:
@@ -97,9 +100,7 @@ const App = () => {
97
  }
98
  });
99
 
100
- // Làm sạch kết quả trả về
101
  let result = output.generated_text;
102
- // Cắt bỏ phần prompt đầu vào (nếu model trả về cả input)
103
  if (result.includes("<|assistant|>")) {
104
  result = result.split("<|assistant|>")[1];
105
  }
@@ -108,9 +109,8 @@ const App = () => {
108
 
109
  } catch (err) {
110
  console.error(err);
111
- setAiError("Lỗi kết nối HF API. Thử lại hoặc tắt chế độ AI.");
112
- // Fallback về manual nếu lỗi
113
- generateManual();
114
  } finally {
115
  setIsLoadingAI(false);
116
  }
@@ -146,7 +146,7 @@ const App = () => {
146
  return (
147
  <div className="min-h-screen bg-slate-950 text-slate-200 font-sans p-4 md:p-8 selection:bg-pink-500/30">
148
 
149
- <header className="max-w-5xl mx-auto mb-8 flex items-center justify-between border-b border-slate-800 pb-4">
150
  <div className="flex items-center gap-3">
151
  <div className="bg-gradient-to-tr from-pink-500 to-purple-600 p-2.5 rounded-xl shadow-lg shadow-purple-900/20">
152
  <FlaskConical className="w-6 h-6 text-white" />
@@ -157,26 +157,41 @@ const App = () => {
157
  </div>
158
  </div>
159
 
160
- {/* Toggle Mode */}
161
- <button
162
- onClick={() => setUseAI(!useAI)}
163
- className={`flex items-center gap-2 px-4 py-2 rounded-full border transition-all text-xs font-bold ${
164
- useAI
165
- ? 'bg-purple-600/20 border-purple-500 text-purple-400 shadow-[0_0_10px_rgba(168,85,247,0.3)]'
166
- : 'bg-slate-900 border-slate-700 text-slate-400'
167
- }`}
168
- >
169
- {useAI ? <BrainCircuit className="w-4 h-4" /> : <Wand2 className="w-4 h-4" />}
170
- {useAI ? 'AI MODE: ON' : 'MANUAL MODE'}
171
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  </header>
173
 
174
  <main className="max-w-5xl mx-auto grid lg:grid-cols-2 gap-8">
175
 
176
  {/* --- LEFT: INPUTS --- */}
177
  <div className="space-y-6">
178
-
179
- {/* 1. Main Idea */}
180
  <div className="space-y-2">
181
  <label className="text-sm font-bold text-slate-300 flex items-center gap-2">
182
  <Sparkles className="w-4 h-4 text-yellow-400" /> Base Concept
@@ -189,7 +204,6 @@ const App = () => {
189
  />
190
  </div>
191
 
192
- {/* 2. Custom Tags */}
193
  <div className="space-y-2">
194
  <label className="text-sm font-bold text-slate-300 flex items-center gap-2">
195
  <Skull className="w-4 h-4 text-red-400" /> Custom Tags / Details
@@ -203,7 +217,6 @@ const App = () => {
203
  />
204
  </div>
205
 
206
- {/* 3. Style */}
207
  <div className="space-y-2">
208
  <label className="text-sm font-bold text-slate-300 flex items-center gap-2">
209
  <Palette className="w-4 h-4 text-blue-400" /> Art Style
@@ -225,7 +238,6 @@ const App = () => {
225
  </div>
226
  </div>
227
 
228
- {/* 4. Options */}
229
  <div className="flex items-center gap-4 p-4 bg-slate-900/50 rounded-xl border border-slate-800">
230
  <div className="flex items-center gap-2 cursor-pointer" onClick={() => setAddQuality(!addQuality)}>
231
  <div className={`w-4 h-4 rounded border flex items-center justify-center transition-colors ${addQuality ? 'bg-pink-600 border-pink-600' : 'border-slate-600 bg-slate-800'}`}>
@@ -234,13 +246,10 @@ const App = () => {
234
  <span className="text-xs font-medium select-none text-slate-300">Auto Quality Boost</span>
235
  </div>
236
  </div>
237
-
238
  </div>
239
 
240
  {/* --- RIGHT: OUTPUT --- */}
241
  <div className="flex flex-col gap-6">
242
-
243
- {/* Action Button */}
244
  <button
245
  onClick={handleGenerate}
246
  disabled={isLoadingAI}
@@ -265,7 +274,6 @@ const App = () => {
265
  </div>
266
  )}
267
 
268
- {/* Result Box */}
269
  <div className="flex-1 bg-black/40 border border-slate-800 rounded-xl p-4 flex flex-col relative group min-h-[200px]">
270
  <label className="text-xs font-bold text-green-400 uppercase mb-2 flex justify-between">
271
  <span>Final Positive Prompt {useAI && "(AI Generated)"}</span>
@@ -298,7 +306,6 @@ const App = () => {
298
  </div>
299
  </div>
300
 
301
- {/* Negative Prompt */}
302
  <div className="bg-slate-900 border border-slate-800 rounded-xl p-4">
303
  <div className="flex items-center justify-between mb-2">
304
  <label className="text-xs font-bold text-red-400 uppercase">Negative Prompt</label>
@@ -325,7 +332,6 @@ const App = () => {
325
  className="w-full h-24 bg-slate-950 border border-slate-700 rounded-lg p-3 text-xs text-slate-400 focus:border-red-500 outline-none resize-none font-mono"
326
  />
327
  </div>
328
-
329
  </div>
330
  </main>
331
  </div>
 
1
  import React, { useState } from 'react';
2
  import {
3
  Sparkles, Copy, Trash2, Wand2, Terminal,
4
+ Palette, Zap, Check, FlaskConical, Skull, BrainCircuit, Loader2, AlertCircle, Key
5
  } from 'lucide-react';
6
+ // SỬA LỖI: Dùng CDN trực tiếp để chạy được ngay trên mọi môi trường
7
+ import { HfInference } from "https://esm.sh/@huggingface/inference";
8
 
9
+ // --- CẤU HÌNH ---
10
+ // ⚠️ QUAN TRỌNG: Để trống dòng này khi commit lên Hugging Face để tránh lỗi lộ secret
11
+ const DEFAULT_HF_TOKEN = "";
 
 
12
 
 
13
  const AI_MODEL = "HuggingFaceH4/zephyr-7b-beta";
14
 
 
15
  const STYLES = [
16
  { id: 'none', label: 'None', prompt: '' },
17
  { id: 'anime', label: 'Anime/Manga', prompt: 'anime style, studio ghibli style, vibrant colors, makoto shinkai style, detailed background' },
 
40
  const [finalPrompt, setFinalPrompt] = useState("");
41
  const [copied, setCopied] = useState(false);
42
 
43
+ // State mới cho chế độ AI và Token
44
  const [useAI, setUseAI] = useState(false);
45
+ const [userToken, setUserToken] = useState(DEFAULT_HF_TOKEN);
46
  const [isLoadingAI, setIsLoadingAI] = useState(false);
47
  const [aiError, setAiError] = useState("");
48
 
 
62
  const generateWithAI = async () => {
63
  if (!basePrompt.trim()) return;
64
 
65
+ // Kiểm tra Token nếu dùng AI Mode
66
+ const tokenToUse = userToken.trim() || DEFAULT_HF_TOKEN;
67
+ // Nếu không có token, vẫn thử gọi (có thể bị rate limit ở chế độ public)
68
+
69
  setIsLoadingAI(true);
70
  setAiError("");
71
+ setFinalPrompt("");
72
 
73
  try {
74
+ const hf = new HfInference(tokenToUse || undefined);
75
+
76
  const styleText = selectedStyle.id !== 'none' ? `Style: ${selectedStyle.label} (${selectedStyle.prompt}).` : "";
77
  const qualityText = addQuality ? "Ensure high quality, detailed, 8k resolution." : "";
78
  const customText = customTags ? `Include these elements: ${customTags}.` : "";
79
 
 
80
  const systemPrompt = `You are an expert Stable Diffusion prompt engineer.
81
  Task: Rewrite the user's concept into a highly detailed, comma-separated prompt optimized for SDXL.
82
  Rules:
 
100
  }
101
  });
102
 
 
103
  let result = output.generated_text;
 
104
  if (result.includes("<|assistant|>")) {
105
  result = result.split("<|assistant|>")[1];
106
  }
 
109
 
110
  } catch (err) {
111
  console.error(err);
112
+ setAiError("Lỗi AI: " + (err.message || "Kết nối thất bại."));
113
+ generateManual(); // Fallback
 
114
  } finally {
115
  setIsLoadingAI(false);
116
  }
 
146
  return (
147
  <div className="min-h-screen bg-slate-950 text-slate-200 font-sans p-4 md:p-8 selection:bg-pink-500/30">
148
 
149
+ <header className="max-w-5xl mx-auto mb-8 flex flex-col md:flex-row items-center justify-between border-b border-slate-800 pb-4 gap-4">
150
  <div className="flex items-center gap-3">
151
  <div className="bg-gradient-to-tr from-pink-500 to-purple-600 p-2.5 rounded-xl shadow-lg shadow-purple-900/20">
152
  <FlaskConical className="w-6 h-6 text-white" />
 
157
  </div>
158
  </div>
159
 
160
+ <div className="flex items-center gap-3">
161
+ {/* Ô nhập Token (Ẩn nếu không dùng AI) */}
162
+ {useAI && (
163
+ <div className="relative group">
164
+ <input
165
+ type="password"
166
+ placeholder="HF Token (Optional)"
167
+ value={userToken}
168
+ onChange={(e) => setUserToken(e.target.value)}
169
+ className="bg-slate-900 border border-slate-700 rounded-full px-3 py-2 text-xs w-32 focus:w-48 transition-all outline-none focus:border-purple-500"
170
+ />
171
+ <div className="absolute right-3 top-2.5 text-slate-500 pointer-events-none">
172
+ <Key className="w-3 h-3" />
173
+ </div>
174
+ </div>
175
+ )}
176
+
177
+ <button
178
+ onClick={() => setUseAI(!useAI)}
179
+ className={`flex items-center gap-2 px-4 py-2 rounded-full border transition-all text-xs font-bold ${
180
+ useAI
181
+ ? 'bg-purple-600/20 border-purple-500 text-purple-400 shadow-[0_0_10px_rgba(168,85,247,0.3)]'
182
+ : 'bg-slate-900 border-slate-700 text-slate-400 hover:bg-slate-800'
183
+ }`}
184
+ >
185
+ {useAI ? <BrainCircuit className="w-4 h-4" /> : <Wand2 className="w-4 h-4" />}
186
+ {useAI ? 'AI MODE: ON' : 'MANUAL MODE'}
187
+ </button>
188
+ </div>
189
  </header>
190
 
191
  <main className="max-w-5xl mx-auto grid lg:grid-cols-2 gap-8">
192
 
193
  {/* --- LEFT: INPUTS --- */}
194
  <div className="space-y-6">
 
 
195
  <div className="space-y-2">
196
  <label className="text-sm font-bold text-slate-300 flex items-center gap-2">
197
  <Sparkles className="w-4 h-4 text-yellow-400" /> Base Concept
 
204
  />
205
  </div>
206
 
 
207
  <div className="space-y-2">
208
  <label className="text-sm font-bold text-slate-300 flex items-center gap-2">
209
  <Skull className="w-4 h-4 text-red-400" /> Custom Tags / Details
 
217
  />
218
  </div>
219
 
 
220
  <div className="space-y-2">
221
  <label className="text-sm font-bold text-slate-300 flex items-center gap-2">
222
  <Palette className="w-4 h-4 text-blue-400" /> Art Style
 
238
  </div>
239
  </div>
240
 
 
241
  <div className="flex items-center gap-4 p-4 bg-slate-900/50 rounded-xl border border-slate-800">
242
  <div className="flex items-center gap-2 cursor-pointer" onClick={() => setAddQuality(!addQuality)}>
243
  <div className={`w-4 h-4 rounded border flex items-center justify-center transition-colors ${addQuality ? 'bg-pink-600 border-pink-600' : 'border-slate-600 bg-slate-800'}`}>
 
246
  <span className="text-xs font-medium select-none text-slate-300">Auto Quality Boost</span>
247
  </div>
248
  </div>
 
249
  </div>
250
 
251
  {/* --- RIGHT: OUTPUT --- */}
252
  <div className="flex flex-col gap-6">
 
 
253
  <button
254
  onClick={handleGenerate}
255
  disabled={isLoadingAI}
 
274
  </div>
275
  )}
276
 
 
277
  <div className="flex-1 bg-black/40 border border-slate-800 rounded-xl p-4 flex flex-col relative group min-h-[200px]">
278
  <label className="text-xs font-bold text-green-400 uppercase mb-2 flex justify-between">
279
  <span>Final Positive Prompt {useAI && "(AI Generated)"}</span>
 
306
  </div>
307
  </div>
308
 
 
309
  <div className="bg-slate-900 border border-slate-800 rounded-xl p-4">
310
  <div className="flex items-center justify-between mb-2">
311
  <label className="text-xs font-bold text-red-400 uppercase">Negative Prompt</label>
 
332
  className="w-full h-24 bg-slate-950 border border-slate-700 rounded-lg p-3 text-xs text-slate-400 focus:border-red-500 outline-none resize-none font-mono"
333
  />
334
  </div>
 
335
  </div>
336
  </main>
337
  </div>