auto-save 2026-05-13 00:28 (~4)
This commit is contained in:
@@ -531,6 +531,13 @@
|
||||
"message": "auto-save 2026-05-13 00:17 (~1)",
|
||||
"hash": "2d7c6cc",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-13T00:23:01+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-13 00:22 (~4)",
|
||||
"hash": "66fb144",
|
||||
"files_changed": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -597,7 +597,8 @@ def get_frame(job_id: str, idx: int):
|
||||
|
||||
class GenerateReq(BaseModel):
|
||||
prompt: str
|
||||
extra_prompt: str = ""
|
||||
extra_prompt: str = "" # ✓ 需要的元素(正向)
|
||||
negative_prompt: str = "" # ✗ 不需要的元素(负向)
|
||||
model: str = "" # 留空用 IMAGE_MODEL 默认
|
||||
mode: str = "edit" # "edit" 带参考图,"text" 纯文字
|
||||
|
||||
@@ -617,7 +618,9 @@ def generate_image(job_id: str, idx: int, req: GenerateReq) -> Job:
|
||||
|
||||
full_prompt = req.prompt.strip()
|
||||
if req.extra_prompt.strip():
|
||||
full_prompt = f"{full_prompt}. {req.extra_prompt.strip()}"
|
||||
full_prompt = f"{full_prompt}. Include: {req.extra_prompt.strip()}"
|
||||
if req.negative_prompt.strip():
|
||||
full_prompt = f"{full_prompt}. Do NOT include: {req.negative_prompt.strip()}. Output must be clean without any watermark, username text, or platform logo."
|
||||
if not full_prompt:
|
||||
raise HTTPException(400, "prompt required")
|
||||
|
||||
|
||||
@@ -580,6 +580,7 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
|
||||
onJobUpdate: (j: Job) => void
|
||||
}) {
|
||||
const [extra, setExtra] = useState("")
|
||||
const [negative, setNegative] = useState("水印, @用户名, TikTok logo, 平台文字, 浮水印")
|
||||
const [model, setModel] = useState("gemini-3-pro-image-preview")
|
||||
const [mode, setMode] = useState<"edit" | "text">("edit")
|
||||
const [generating, setGenerating] = useState(false)
|
||||
@@ -605,6 +606,7 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
|
||||
const updated = await generateImage(job.id, frame.index, {
|
||||
prompt: editablePrompt,
|
||||
extra_prompt: extra,
|
||||
negative_prompt: negative,
|
||||
model,
|
||||
mode,
|
||||
})
|
||||
@@ -660,14 +662,14 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* base prompt(可展开编辑) */}
|
||||
{/* 画面描述(AI 自动 · 可展开编辑) */}
|
||||
<div className="mt-2.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPrompt((v) => !v)}
|
||||
className="w-full text-[10px] text-[var(--text-faint)] hover:text-[var(--text-strong)] inline-flex items-center justify-between"
|
||||
>
|
||||
<span>基础 prompt {showPrompt ? "▼" : "▶"}</span>
|
||||
<span>画面描述(AI 自动 · 看图给的英文 prompt) {showPrompt ? "▼" : "▶"}</span>
|
||||
<span className="font-mono">{editablePrompt.length} 字符</span>
|
||||
</button>
|
||||
{showPrompt && (
|
||||
@@ -680,18 +682,41 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 用户额外指令 */}
|
||||
{/* 正向:我要保留 / 加入 */}
|
||||
<div className="mt-2">
|
||||
<div className="text-[10px] text-[var(--text-faint)] uppercase tracking-widest mb-1">我的需求(额外指令)</div>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-[10px] uppercase tracking-widest text-emerald-300">✓ 需要保留 / 加入</span>
|
||||
</div>
|
||||
<textarea
|
||||
value={extra}
|
||||
onChange={(e) => setExtra(e.target.value)}
|
||||
rows={2}
|
||||
placeholder="例:加 SKG logo、换实验室背景、删水印"
|
||||
className="w-full text-[11.5px] px-2 py-1.5 rounded-md bg-black/30 border border-white/15 text-[var(--text-strong)] placeholder:text-[var(--text-faint)] resize-none focus:ring-2 focus:ring-rose-400/40 outline-none"
|
||||
placeholder="例:加 SKG logo、保留瓶子、换实验室背景"
|
||||
className="w-full text-[11.5px] px-2 py-1.5 rounded-md bg-emerald-500/5 border border-emerald-400/30 text-[var(--text-strong)] placeholder:text-[var(--text-faint)] resize-none focus:ring-2 focus:ring-emerald-400/40 outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 负向:不需要 / 删除 */}
|
||||
<div className="mt-2">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-[10px] uppercase tracking-widest text-rose-300">✗ 不要 / 删除</span>
|
||||
<button
|
||||
onClick={() => setNegative("")}
|
||||
className="text-[9px] text-[var(--text-faint)] hover:text-[var(--text-strong)]"
|
||||
>
|
||||
清空
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
value={negative}
|
||||
onChange={(e) => setNegative(e.target.value)}
|
||||
rows={2}
|
||||
placeholder="例:水印、@用户名、TikTok logo、文字遮罩"
|
||||
className="w-full text-[11.5px] px-2 py-1.5 rounded-md bg-rose-500/5 border border-rose-400/30 text-[var(--text-strong)] placeholder:text-[var(--text-faint)] resize-none focus:ring-2 focus:ring-rose-400/40 outline-none"
|
||||
/>
|
||||
<div className="text-[9.5px] text-[var(--text-faint)] mt-0.5">默认已清除水印 / 平台元素 → 生成"纯净图"</div>
|
||||
</div>
|
||||
|
||||
{/* 模型 + 模式 + 生成 */}
|
||||
<div className="mt-2 flex gap-1.5 items-center">
|
||||
<select
|
||||
|
||||
@@ -131,7 +131,7 @@ export async function describeFrame(jobId: string, frameIdx: number): Promise<Jo
|
||||
export async function generateImage(
|
||||
jobId: string,
|
||||
frameIdx: number,
|
||||
body: { prompt: string; extra_prompt?: string; model?: string; mode?: "edit" | "text" },
|
||||
body: { prompt: string; extra_prompt?: string; negative_prompt?: string; model?: string; mode?: "edit" | "text" },
|
||||
): Promise<Job> {
|
||||
const res = await fetch(`${API_BASE}/jobs/${jobId}/frames/${frameIdx}/generate`, {
|
||||
method: "POST",
|
||||
|
||||
Reference in New Issue
Block a user