fix: split generation workspace panels
This commit is contained in:
@@ -1489,6 +1489,20 @@
|
||||
"message": "fix: loosen glass dashboard workspace",
|
||||
"hash": "7fcda19",
|
||||
"files_changed": 8
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-20T09:49:16+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-20 09:49 (~3)",
|
||||
"hash": "ccbfd3e",
|
||||
"files_changed": 3
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-20T09:54:43+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-20 09:54 (~3)",
|
||||
"hash": "7ad323a",
|
||||
"files_changed": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -354,6 +354,40 @@ input, textarea {
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.session-workspace {
|
||||
height: calc(100vh - 116px);
|
||||
min-height: 640px;
|
||||
}
|
||||
|
||||
.session-split {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(360px, 430px);
|
||||
gap: 18px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.session-image-pane,
|
||||
.pack-scroll {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
|
||||
}
|
||||
|
||||
.session-image-pane::-webkit-scrollbar,
|
||||
.pack-scroll::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.session-image-pane::-webkit-scrollbar-thumb,
|
||||
.pack-scroll::-webkit-scrollbar-thumb {
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
}
|
||||
|
||||
.session-pack-pane {
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.08);
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
.result-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
|
||||
@@ -368,6 +402,41 @@ input, textarea {
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1760px) {
|
||||
.session-split {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(400px, 480px);
|
||||
gap: 22px;
|
||||
}
|
||||
|
||||
.session-pack-pane {
|
||||
padding-left: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.session-workspace {
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.session-split {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.session-image-pane,
|
||||
.session-pack-pane {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.session-pack-pane {
|
||||
border-left: 0;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||
padding-left: 0;
|
||||
padding-top: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Login (cloned from SKG source) ===== */
|
||||
.login-page {
|
||||
background:
|
||||
|
||||
@@ -353,8 +353,8 @@ export default function Home() {
|
||||
/>
|
||||
)}
|
||||
{current && (
|
||||
<section className="dashboard-workbench space-y-5 p-5">
|
||||
<div className="flex items-end justify-between gap-4">
|
||||
<section className="dashboard-workbench session-workspace flex flex-col gap-5 p-5">
|
||||
<div className="flex shrink-0 items-end justify-between gap-4">
|
||||
<div>
|
||||
<span className="section-eyebrow">Step · 02 · Quick Screen</span>
|
||||
<h2 className="mt-2 text-lg font-semibold text-white">本次生成</h2>
|
||||
@@ -364,7 +364,11 @@ export default function Home() {
|
||||
</div>
|
||||
<code className="max-w-[220px] truncate text-[11px] text-white/30 font-mono">{current.id}</code>
|
||||
</div>
|
||||
<div className="session-split min-h-0 flex-1">
|
||||
<div className="session-image-pane min-h-0 overflow-y-auto pr-1">
|
||||
<ResultGrid images={current.images} onAction={handleAction} />
|
||||
</div>
|
||||
<aside className="session-pack-pane min-h-0 overflow-hidden">
|
||||
<PackPanel
|
||||
session={current}
|
||||
loadingKind={loadingKind}
|
||||
@@ -377,6 +381,8 @@ export default function Home() {
|
||||
onRegenerateAsset={handleRegenerateAsset}
|
||||
onGenerateVideo={handleGenerateVideo}
|
||||
/>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -60,10 +60,10 @@ function AssetRow({ template, asset, accent, onRegenerate }: {
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="grid grid-cols-[96px_minmax(0,1fr)_minmax(132px,auto)] gap-3 p-3 rounded-[8px] bg-white/[0.035] ring-1 ring-white/[0.06] hover:bg-white/[0.05] hover:ring-white/[0.12] transition-all">
|
||||
<div className="grid grid-cols-[72px_minmax(0,1fr)_minmax(78px,auto)] gap-3 p-3 rounded-[8px] bg-white/[0.035] ring-1 ring-white/[0.06] hover:bg-white/[0.05] hover:ring-white/[0.12] transition-all 2xl:grid-cols-[84px_minmax(0,1fr)_minmax(104px,auto)]">
|
||||
{/* thumbnail */}
|
||||
<div
|
||||
className="relative flex w-[92px] max-h-[110px] items-center justify-center overflow-visible rounded-[8px] bg-black/30 ring-1 ring-white/[0.08]"
|
||||
className="relative flex w-[70px] max-h-[86px] items-center justify-center overflow-visible rounded-[8px] bg-black/30 ring-1 ring-white/[0.08] 2xl:w-[82px] 2xl:max-h-[98px]"
|
||||
style={{ aspectRatio: aspectCss(asset?.aspectRatio ?? template.aspectRatio) }}
|
||||
>
|
||||
{ready ? (
|
||||
@@ -124,7 +124,7 @@ function AssetRow({ template, asset, accent, onRegenerate }: {
|
||||
{ready && onRegenerate && (
|
||||
<button
|
||||
onClick={() => setShowRedo(value => !value)}
|
||||
className="mt-1 rounded-[8px] bg-white/[0.055] px-2.5 py-1.5 text-[10px] text-white/60 ring-1 ring-white/[0.08] transition-colors hover:bg-white/[0.10] hover:text-white"
|
||||
className="mt-1 rounded-[8px] bg-white/[0.055] px-2 py-1.5 text-[10px] text-white/60 ring-1 ring-white/[0.08] transition-colors hover:bg-white/[0.10] hover:text-white 2xl:px-2.5"
|
||||
>
|
||||
{showRedo ? '收起重做' : '重做'}
|
||||
</button>
|
||||
@@ -475,9 +475,13 @@ export default function PackPanel({
|
||||
|
||||
if (!primaryImage) {
|
||||
return (
|
||||
<section className="card p-6">
|
||||
<section className="card h-full p-6">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-8 h-8 rounded-[8px] bg-white/[0.05] border border-white/[0.08] flex items-center justify-center text-white/30 text-sm">⌗</div>
|
||||
<div className="w-8 h-8 rounded-[8px] bg-white/[0.05] border border-white/[0.08] flex items-center justify-center text-white/30 text-sm">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" aria-hidden="true">
|
||||
<path d="M4 7h16M7 4v16M17 4v16M4 17h16" strokeLinecap="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<span className="section-eyebrow">Step · 03 · Assets</span>
|
||||
<h2 className="mt-1.5 text-sm font-semibold text-white">选中主方案后展开资产清单</h2>
|
||||
@@ -493,10 +497,10 @@ export default function PackPanel({
|
||||
const generatedTotal = packs.reduce((s, p) => s + p.assets.length, 0);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex h-full min-h-0 flex-col gap-4">
|
||||
{/* Step 03 header card */}
|
||||
<section className="card p-5 space-y-4">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<section className="card shrink-0 space-y-4 p-4">
|
||||
<div className="flex flex-col gap-4 2xl:flex-row 2xl:items-start 2xl:justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="section-eyebrow">Step · 03 · Lock & Generate</span>
|
||||
<h2 className="mt-1.5 text-base font-semibold text-white">角色锁定 & 资产清单</h2>
|
||||
@@ -505,7 +509,7 @@ export default function PackPanel({
|
||||
</p>
|
||||
</div>
|
||||
{/* primary image + stats */}
|
||||
<div className="flex items-center gap-4 shrink-0">
|
||||
<div className="flex items-center justify-between gap-4 shrink-0 2xl:justify-end">
|
||||
<div className="text-right text-[11px] text-white/40 space-y-0.5">
|
||||
<div className="font-mono text-white/70">{generatedTotal} <span className="text-white/30">/ {totalImageSlots} 张</span></div>
|
||||
<div className="text-[10px]">图片位</div>
|
||||
@@ -519,11 +523,11 @@ export default function PackPanel({
|
||||
</div>
|
||||
|
||||
{/* action buttons */}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="grid grid-cols-1 gap-2 2xl:grid-cols-2">
|
||||
<button
|
||||
onClick={() => onLockCharacter(primaryImage)}
|
||||
disabled={characterLoading || !!loadingKind || allLoading}
|
||||
className="btn btn-glass text-xs disabled:opacity-40"
|
||||
className="btn btn-glass justify-center text-xs disabled:opacity-40"
|
||||
>
|
||||
{characterLoading ? (
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" className="animate-spin" fill="none" stroke="currentColor" strokeWidth="2.5">
|
||||
@@ -543,7 +547,7 @@ export default function PackPanel({
|
||||
if (ok) onGenerateAll(primaryImage);
|
||||
}}
|
||||
disabled={allLoading || !!loadingKind || characterLoading}
|
||||
className="btn btn-primary text-xs disabled:opacity-40"
|
||||
className="btn btn-primary justify-center text-xs disabled:opacity-40"
|
||||
>
|
||||
{allLoading ? (
|
||||
<>
|
||||
@@ -590,6 +594,7 @@ export default function PackPanel({
|
||||
<SectionNav active={activeNav} onChange={setActiveNav} />
|
||||
</section>
|
||||
|
||||
<div className="pack-scroll min-h-0 flex-1 space-y-3 overflow-y-auto pr-1">
|
||||
{/* Pack sections */}
|
||||
{PACK_ORDER.map(kind => {
|
||||
const pack = packs.find(p => p.kind === kind && p.sourceImageId === primaryImage.id);
|
||||
@@ -611,5 +616,6 @@ export default function PackPanel({
|
||||
<TextTemplateSection />
|
||||
<VideoSection videoLoading={videoLoading} primaryImage={primaryImage} onGenerateVideo={onGenerateVideo} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function ResultGrid({ images, onAction }: ResultGridProps) {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="sticky top-0 z-20 -mx-1 flex flex-wrap items-center justify-between gap-3 rounded-[8px] bg-[#202020]/82 px-1 py-2 backdrop-blur-xl">
|
||||
<div className="flex items-center gap-2 text-[11px] text-white/55">
|
||||
<kbd className="kbd">1</kbd>
|
||||
<span className="text-white/40">–</span>
|
||||
|
||||
Reference in New Issue
Block a user