auto-save 2026-05-13 00:28 (~4)

This commit is contained in:
2026-05-13 00:28:31 +08:00
parent 66fb1444c4
commit 2512a2b957
4 changed files with 44 additions and 9 deletions

View File

@@ -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
}
]
}

View File

@@ -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")

View File

@@ -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

View File

@@ -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",