diff --git a/.memory/worklog.json b/.memory/worklog.json index d1a6c3f..1dc3460 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1545,6 +1545,34 @@ "message": "auto-save 2026-05-20 14:45 (~4)", "hash": "527ccfa", "files_changed": 4 + }, + { + "ts": "2026-05-20T16:33:38+08:00", + "type": "commit", + "message": "auto-save 2026-05-20 16:33 (~2)", + "hash": "9b1833c", + "files_changed": 2 + }, + { + "ts": "2026-05-20T16:39:05+08:00", + "type": "commit", + "message": "auto-save 2026-05-20 16:39 (~3)", + "hash": "e2465d8", + "files_changed": 3 + }, + { + "ts": "2026-05-20T16:44:32+08:00", + "type": "commit", + "message": "auto-save 2026-05-20 16:44 (~2)", + "hash": "3b04b72", + "files_changed": 2 + }, + { + "ts": "2026-05-20T16:48:16+08:00", + "type": "commit", + "message": "fix: improve project brief preview layout", + "hash": "61e597c", + "files_changed": 3 } ] } diff --git a/src/app/globals.css b/src/app/globals.css index 015d50e..151c55f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -479,7 +479,7 @@ input, textarea { .project-board-grid { display: grid; - grid-template-columns: minmax(250px, 310px) minmax(0, 1fr) 88px; + grid-template-columns: clamp(330px, 22vw, 410px) minmax(0, 1fr) 88px; gap: 14px; min-height: 0; flex: 1; @@ -499,18 +499,90 @@ input, textarea { padding: 16px; } +.project-primary-card { + position: relative; + border-radius: 8px; + background: + linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.028)), + rgba(0,0,0,0.18); + box-shadow: + 0 20px 60px -40px rgba(230,245,120,0.72), + inset 0 0 0 1px rgba(255,255,255,0.08); + padding: 9px; +} + .project-primary-preview { position: relative; display: grid; place-items: center; overflow: hidden; + border-radius: 6px; + background: + linear-gradient(135deg, rgba(255,255,255,0.98), rgba(238,242,230,0.93)); + aspect-ratio: 1 / 1; + min-height: 260px; + max-height: min(42vh, 460px); + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.52); +} + +.project-primary-image { + width: 100%; + height: 100%; + object-fit: contain; + transform: scale(0.98); + transition: transform 200ms ease, filter 200ms ease; +} + +.project-primary-card:hover .project-primary-image { + filter: contrast(1.02) saturate(1.02); + transform: scale(1.015); +} + +.project-primary-meta { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 9px 2px 1px; +} + +.project-image-popover { + position: fixed; + z-index: 120; + top: 50%; + left: 50%; + width: min(76vw, 920px); + height: min(82vh, 780px); + display: grid; + place-items: center; + border: 1px solid rgba(255,255,255,0.16); border-radius: 8px; - background: rgba(255,255,255,0.92); - aspect-ratio: 4 / 3; - min-height: 190px; - box-shadow: - 0 20px 60px -38px rgba(230,245,120,0.7), - inset 0 0 0 1px rgba(255,255,255,0.36); + background: + linear-gradient(135deg, rgba(255,255,255,0.10), rgba(255,255,255,0.035)), + rgba(9, 10, 8, 0.86); + box-shadow: 0 30px 120px -28px rgba(0,0,0,0.94); + opacity: 0; + overflow: hidden; + pointer-events: none; + transform: translate(-50%, -48%) scale(0.96); + transition: opacity 180ms ease, transform 180ms ease; + visibility: hidden; +} + +.project-image-popover--open { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + visibility: visible; +} + +.project-image-popover img { + position: absolute; + inset: 18px; + width: calc(100% - 36px); + height: calc(100% - 36px); + object-fit: contain; + border-radius: 6px; + background: rgba(255,255,255,0.96); } .project-stat { @@ -534,6 +606,31 @@ input, textarea { padding: 13px; } +.project-spec-row { + display: grid; + grid-template-columns: 42px minmax(0, 1fr); + gap: 10px; + align-items: start; + border-top: 1px solid rgba(255,255,255,0.06); + padding-top: 8px; +} + +.project-spec-row:first-child { + border-top: 0; + padding-top: 0; +} + +.project-spec-label { + color: rgba(255,255,255,0.36); + line-height: 1.55; +} + +.project-spec-value { + color: rgba(255,255,255,0.78); + line-height: 1.55; + overflow-wrap: anywhere; +} + .project-production-panel { min-height: 0; overflow: hidden; @@ -642,11 +739,22 @@ input, textarea { } .project-board-grid { - grid-template-columns: minmax(280px, 340px) minmax(0, 1fr) 96px; + grid-template-columns: clamp(370px, 22vw, 440px) minmax(0, 1fr) 96px; gap: 18px; } } +@media (prefers-reduced-motion: reduce) { + .project-primary-image, + .project-image-popover { + transition: none; + } + + .project-primary-card:hover .project-primary-image { + transform: none; + } +} + @media (max-width: 1180px) { .project-shell { overflow: auto; @@ -677,6 +785,14 @@ input, textarea { overflow: visible; } + .project-primary-preview { + min-height: 220px; + } + + .project-image-popover { + display: none; + } + .session-workspace { height: auto; min-height: 0; diff --git a/src/app/page.tsx b/src/app/page.tsx index e265162..3bc33c1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,7 @@ 'use client'; import { useCallback, useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; import PromptPanel from '@/components/PromptPanel'; import Sidebar from '@/components/Sidebar'; import PackPanel from '@/components/PackPanel'; @@ -55,6 +56,18 @@ function ProjectStat({ label, value, tone }: { label: string; value: string | nu ); } +function ProjectSpecField({ label, value }: { label: string; value?: string | string[] }) { + const text = Array.isArray(value) ? value.filter(Boolean).join('、') : value; + if (!text) return null; + + return ( +
+ {label} + {text} +
+ ); +} + function ReferenceStrip({ session }: { session: GenSession }) { const refs = imageSourcesForSession(session); if (!refs.length) return null; @@ -76,6 +89,7 @@ function ProjectBrief({ session }: { session: GenSession }) { const primaryImage = selectedImages[0] ?? session.images[0] ?? null; const generatedAssets = (session.packs ?? []).reduce((sum, pack) => sum + pack.assets.length, 0); const totalSlots = packSlotTotal(); + const [previewOpen, setPreviewOpen] = useState(false); return (
@@ -93,12 +107,26 @@ function ProjectBrief({ session }: { session: GenSession }) { {primaryImage && ( -
- 当前主方案 -
+
setPreviewOpen(true)} + onMouseLeave={() => setPreviewOpen(false)} + onFocus={() => setPreviewOpen(true)} + onBlur={() => setPreviewOpen(false)} + > +
+ 当前主方案 +
+
Primary {primaryImage.status === 'selected' ? '已选中' : '待筛选'}
+ {previewOpen && typeof document !== 'undefined' && createPortal( + , + document.body, + )}
)} @@ -115,17 +143,14 @@ function ProjectBrief({ session }: { session: GenSession }) { 已锁定
- {[ - ['形态', session.characterSpec.speciesShape], - ['比例', session.characterSpec.bodyRatio], - ['配色', session.characterSpec.colorPalette.join('、')], - ['材料', session.characterSpec.materials.join('、')], - ].map(([label, value]) => ( -
- {label} - {value} -
- ))} + + + + + + + +
) : (