feat(web): polish custom style view layout and UX

Rework custom style view: fixed modal height to match grid view, move
upload and preset-import controls to bottom toolbar alongside cancel/save,
textarea fills remaining space. Add bordered style to cancel button,
improve disabled save button visibility, remove per-card magic-wand
customize button, and add placeholder hint about English prompts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yuanzonghao
2026-06-06 21:47:10 +08:00
parent 7185f319a2
commit 9a1c292b77
+69 -81
View File
@@ -1025,7 +1025,7 @@ function StyleModal({
<div <div
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
className={ className={
"flex w-[1400px] max-w-[94vw] max-h-[86vh] flex-col overflow-hidden rounded-sm border border-clay-900/15 bg-cream-50 shadow-2xl shadow-clay-900/25 transition-all duration-300 " + "flex w-[1400px] max-w-[94vw] h-[86vh] flex-col overflow-hidden rounded-sm border border-clay-900/15 bg-cream-50 shadow-2xl shadow-clay-900/25 transition-all duration-300 " +
(shown ? "opacity-100 scale-100" : "opacity-0 scale-95") (shown ? "opacity-100 scale-100" : "opacity-0 scale-95")
} }
> >
@@ -1073,53 +1073,56 @@ function StyleModal({
</div> </div>
{view === "custom" ? ( {view === "custom" ? (
<div className="flex flex-col gap-4 overflow-y-auto px-6 py-6 md:px-8"> <div className="flex flex-1 flex-col gap-4 overflow-y-auto px-6 py-6 md:px-8">
<div className="flex flex-col gap-2"> <input
<input ref={fileInputRef}
ref={fileInputRef} type="file"
type="file" accept="image/*"
accept="image/*" className="hidden"
className="hidden" onChange={(e) => {
onChange={(e) => { const f = e.target.files?.[0];
const f = e.target.files?.[0]; if (f) handleUploadStyleImage(f);
if (f) handleUploadStyleImage(f); if (fileInputRef.current) fileInputRef.current.value = "";
if (fileInputRef.current) fileInputRef.current.value = ""; }}
}} />
/> <textarea
value={draft}
onChange={(e) => setDraft(e.target.value)}
autoFocus
rows={6}
placeholder={"描述你想要的画面风格,例如:\n梦幻水彩风格,柔和的色调,怀旧的氛围\n\n💡 提示:部分绘图模型对英文提示词效果更佳,建议先借助 AI 对话工具生成专业的英文风格描述,再粘贴到这里"}
className="w-full flex-1 resize-y rounded-sm border border-clay-900/15 bg-cream-50 px-3 py-2.5 font-sans text-[13px] leading-relaxed text-clay-900 outline-none transition-colors focus:border-ember-500 placeholder:text-clay-400"
/>
{parseError && (
<span className="font-sans text-[11px] text-rose-500">
<i className="fa-solid fa-circle-exclamation mr-1" />
{parseError}
</span>
)}
<div className="flex items-center gap-2">
{customStyleRefImage ? ( {customStyleRefImage ? (
<div className="flex items-center gap-3 rounded-sm border border-clay-900/12 bg-cream-100 px-3 py-2.5"> <div className="flex items-center gap-2">
{/* eslint-disable-next-line @next/next/no-img-element */} {/* eslint-disable-next-line @next/next/no-img-element */}
<img <img
src={customStyleRefImage} src={customStyleRefImage}
alt="画风参考图" alt="画风参考图"
className="h-14 w-14 shrink-0 rounded-sm border border-clay-900/10 object-cover" className="h-8 w-8 shrink-0 rounded-sm border border-clay-900/10 object-cover"
/> />
<div className="flex min-w-0 flex-1 flex-col"> <button
<span className="font-sans text-[12px] text-clay-900"> type="button"
<i className="fa-solid fa-check mr-1.5 text-ember-500" /> onClick={() => fileInputRef.current?.click()}
disabled={parsing}
</span> className="font-sans text-[11px] text-clay-500 hover:text-ember-500 transition-colors disabled:opacity-50"
<span className="font-sans text-[11px] leading-snug text-clay-500"> >
AI
</span> </button>
</div> <button
<div className="flex flex-col items-end gap-1"> type="button"
<button onClick={() => removeStyleRefImage()}
type="button" className="font-sans text-[11px] text-clay-400 hover:text-clay-900 transition-colors"
onClick={() => fileInputRef.current?.click()} >
disabled={parsing}
className="font-sans text-[11px] text-clay-500 hover:text-ember-500 transition-colors disabled:opacity-50" </button>
>
</button>
<button
type="button"
onClick={() => removeStyleRefImage()}
className="font-sans text-[11px] text-clay-400 hover:text-clay-900 transition-colors"
>
</button>
</div>
</div> </div>
) : ( ) : (
<button <button
@@ -1127,45 +1130,43 @@ function StyleModal({
onClick={() => fileInputRef.current?.click()} onClick={() => fileInputRef.current?.click()}
disabled={parsing} disabled={parsing}
className={ className={
"flex items-center justify-center gap-2 rounded-sm border border-dashed px-3 py-2.5 font-sans text-[12px] transition-colors " + "flex items-center gap-1.5 rounded-sm border px-3 py-1.5 font-sans text-[12px] transition-colors " +
(parsing (parsing
? "border-clay-900/15 bg-cream-100 text-clay-400 cursor-wait" ? "border-clay-900/15 text-clay-400 cursor-wait"
: "border-clay-900/25 text-clay-700 hover:border-ember-500 hover:bg-ember-500/5 hover:text-ember-500") : "border-clay-900/15 text-clay-700 hover:border-ember-500 hover:text-ember-500")
} }
> >
{parsing ? ( {parsing ? (
<> <>
<i className="fa-solid fa-circle-notch fa-spin text-[11px]" /> <i className="fa-solid fa-circle-notch fa-spin text-[11px]" />
AI
</> </>
) : ( ) : (
<> <>
<i className="fa-regular fa-image text-[13px]" /> <i className="fa-regular fa-image text-[11px]" />
· AI
</> </>
)} )}
</button> </button>
)} )}
{parseError && ( <select
<span className="font-sans text-[11px] text-rose-500"> value=""
<i className="fa-solid fa-circle-exclamation mr-1" /> onChange={(e) => {
{parseError} const v = e.target.value;
</span> if (v && STYLE_MAP[v]) setDraft(STYLE_MAP[v]);
)} }}
</div> className="h-8 w-44 rounded-sm border border-clay-900/15 bg-cream-50 px-2 font-sans text-[12px] text-clay-700 outline-none transition-colors focus:border-ember-500"
<textarea >
value={draft} <option value=""></option>
onChange={(e) => setDraft(e.target.value)} {Object.keys(STYLE_MAP).map((s) => (
autoFocus <option key={s} value={s}>{s}</option>
rows={6} ))}
placeholder={"描述你想要的画面风格,例如:\n梦幻水彩风格,柔和的色调,怀旧的氛围"} </select>
className="w-full resize-y rounded-sm border border-clay-900/15 bg-cream-50 px-3 py-2.5 font-sans text-[13px] leading-relaxed text-clay-900 outline-none transition-colors focus:border-ember-500 placeholder:text-clay-400" <div className="flex-1" />
/>
<div className="flex items-center justify-end gap-2">
<button <button
type="button" type="button"
onClick={() => setView("grid")} onClick={() => setView("grid")}
className="px-3 py-1.5 font-sans text-xs text-clay-500 hover:text-clay-900 transition-colors" className="rounded-sm border border-clay-900/15 px-4 py-1.5 font-sans text-xs text-clay-700 hover:border-clay-900/30 hover:text-clay-900 transition-colors"
> >
</button> </button>
@@ -1174,10 +1175,10 @@ function StyleModal({
disabled={!draft.trim()} disabled={!draft.trim()}
onClick={saveCustom} onClick={saveCustom}
className={ className={
"rounded-sm px-4 py-1.5 font-sans text-xs text-cream-50 transition-colors " + "rounded-sm px-4 py-1.5 font-sans text-xs transition-colors " +
(draft.trim() (draft.trim()
? "bg-clay-900 hover:bg-ember-500" ? "bg-clay-900 text-cream-50 hover:bg-ember-500"
: "bg-clay-300 cursor-not-allowed") : "bg-clay-900/20 text-clay-500 cursor-not-allowed")
} }
> >
@@ -1189,7 +1190,6 @@ function StyleModal({
{list.map(({ name, i }) => { {list.map(({ name, i }) => {
const isCustom = name === "自定义风格"; const isCustom = name === "自定义风格";
const isAuto = name === "自动"; const isAuto = name === "自动";
const hasStyleMap = Boolean(STYLE_MAP[name]);
const thumb = STYLE_THUMB[name]; const thumb = STYLE_THUMB[name];
return ( return (
<div <div
@@ -1226,18 +1226,6 @@ function StyleModal({
) : ( ) : (
<div className="absolute inset-0 bg-cream-100" /> <div className="absolute inset-0 bg-cream-100" />
)} )}
{!isAuto && !isCustom && hasStyleMap && (
<span
onClick={(e) => {
e.stopPropagation();
openCustomView(STYLE_MAP[name] ?? "");
}}
title="基于此风格自定义"
className="absolute right-1.5 top-1.5 z-20 flex h-6 w-6 items-center justify-center rounded-sm text-[11px] text-cream-50/70 opacity-0 transition-all group-hover:opacity-100 hover:bg-ember-500/20 hover:text-cream-50"
>
<i className="fa-solid fa-wand-magic-sparkles" />
</span>
)}
</div> </div>
<span className={"block px-2 py-2 text-center font-serif text-sm " + (i === value ? "text-ember-500" : "text-clay-700")}> <span className={"block px-2 py-2 text-center font-serif text-sm " + (i === value ? "text-ember-500" : "text-clay-700")}>
{name} {name}