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)",
|
"message": "auto-save 2026-05-13 00:17 (~1)",
|
||||||
"hash": "2d7c6cc",
|
"hash": "2d7c6cc",
|
||||||
"files_changed": 1
|
"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):
|
class GenerateReq(BaseModel):
|
||||||
prompt: str
|
prompt: str
|
||||||
extra_prompt: str = ""
|
extra_prompt: str = "" # ✓ 需要的元素(正向)
|
||||||
|
negative_prompt: str = "" # ✗ 不需要的元素(负向)
|
||||||
model: str = "" # 留空用 IMAGE_MODEL 默认
|
model: str = "" # 留空用 IMAGE_MODEL 默认
|
||||||
mode: str = "edit" # "edit" 带参考图,"text" 纯文字
|
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()
|
full_prompt = req.prompt.strip()
|
||||||
if req.extra_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:
|
if not full_prompt:
|
||||||
raise HTTPException(400, "prompt required")
|
raise HTTPException(400, "prompt required")
|
||||||
|
|
||||||
|
|||||||
@@ -580,6 +580,7 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
|
|||||||
onJobUpdate: (j: Job) => void
|
onJobUpdate: (j: Job) => void
|
||||||
}) {
|
}) {
|
||||||
const [extra, setExtra] = useState("")
|
const [extra, setExtra] = useState("")
|
||||||
|
const [negative, setNegative] = useState("水印, @用户名, TikTok logo, 平台文字, 浮水印")
|
||||||
const [model, setModel] = useState("gemini-3-pro-image-preview")
|
const [model, setModel] = useState("gemini-3-pro-image-preview")
|
||||||
const [mode, setMode] = useState<"edit" | "text">("edit")
|
const [mode, setMode] = useState<"edit" | "text">("edit")
|
||||||
const [generating, setGenerating] = useState(false)
|
const [generating, setGenerating] = useState(false)
|
||||||
@@ -605,6 +606,7 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
|
|||||||
const updated = await generateImage(job.id, frame.index, {
|
const updated = await generateImage(job.id, frame.index, {
|
||||||
prompt: editablePrompt,
|
prompt: editablePrompt,
|
||||||
extra_prompt: extra,
|
extra_prompt: extra,
|
||||||
|
negative_prompt: negative,
|
||||||
model,
|
model,
|
||||||
mode,
|
mode,
|
||||||
})
|
})
|
||||||
@@ -660,14 +662,14 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* base prompt(可展开编辑) */}
|
{/* 画面描述(AI 自动 · 可展开编辑) */}
|
||||||
<div className="mt-2.5">
|
<div className="mt-2.5">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPrompt((v) => !v)}
|
onClick={() => setShowPrompt((v) => !v)}
|
||||||
className="w-full text-[10px] text-[var(--text-faint)] hover:text-[var(--text-strong)] inline-flex items-center justify-between"
|
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>
|
<span className="font-mono">{editablePrompt.length} 字符</span>
|
||||||
</button>
|
</button>
|
||||||
{showPrompt && (
|
{showPrompt && (
|
||||||
@@ -680,18 +682,41 @@ function ImageGenCard({ job, frame, onJobUpdate }: {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 用户额外指令 */}
|
{/* 正向:我要保留 / 加入 */}
|
||||||
<div className="mt-2">
|
<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
|
<textarea
|
||||||
value={extra}
|
value={extra}
|
||||||
onChange={(e) => setExtra(e.target.value)}
|
onChange={(e) => setExtra(e.target.value)}
|
||||||
rows={2}
|
rows={2}
|
||||||
placeholder="例:加 SKG logo、换实验室背景、删水印"
|
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"
|
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>
|
||||||
|
|
||||||
|
{/* 负向:不需要 / 删除 */}
|
||||||
|
<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">
|
<div className="mt-2 flex gap-1.5 items-center">
|
||||||
<select
|
<select
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export async function describeFrame(jobId: string, frameIdx: number): Promise<Jo
|
|||||||
export async function generateImage(
|
export async function generateImage(
|
||||||
jobId: string,
|
jobId: string,
|
||||||
frameIdx: number,
|
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> {
|
): Promise<Job> {
|
||||||
const res = await fetch(`${API_BASE}/jobs/${jobId}/frames/${frameIdx}/generate`, {
|
const res = await fetch(`${API_BASE}/jobs/${jobId}/frames/${frameIdx}/generate`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
Reference in New Issue
Block a user