diff --git a/.memory/worklog.json b/.memory/worklog.json index 1022f11..75186e2 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1503,6 +1503,27 @@ "message": "auto-save 2026-05-20 09:54 (~3)", "hash": "7ad323a", "files_changed": 3 + }, + { + "ts": "2026-05-20T12:33:03+08:00", + "type": "commit", + "message": "auto-save 2026-05-20 12:33 (+1, ~1)", + "hash": "a62f9b1", + "files_changed": 2 + }, + { + "ts": "2026-05-20T12:38:29+08:00", + "type": "commit", + "message": "auto-save 2026-05-20 12:38 (~3)", + "hash": "a25b740", + "files_changed": 3 + }, + { + "ts": "2026-05-20T12:49:21+08:00", + "type": "commit", + "message": "auto-save 2026-05-20 12:49 (~3)", + "hash": "a190800", + "files_changed": 3 } ] } diff --git a/src/app/globals.css b/src/app/globals.css index 159b92d..54cd7ec 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -361,31 +361,63 @@ input, textarea { .session-split { display: grid; - grid-template-columns: minmax(0, 1fr) minmax(360px, 430px); + grid-template-columns: minmax(0, 1fr) 74px; 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; +.gallery-thumb-list { + scrollbar-width: none; +} + +.gallery-thumb-list::-webkit-scrollbar { + display: none; +} + +.gallery-backdrop { + position: fixed; + inset: 0; + z-index: 70; + background: rgba(0, 0, 0, 0.42); + backdrop-filter: blur(6px); + transition: opacity 180ms ease; +} + +.gallery-drawer { + position: fixed; + z-index: 80; + top: 0; + right: 0; + height: 100vh; + width: min(820px, calc(100vw - 96px)); + display: flex; + flex-direction: column; + border-left: 1px solid rgba(255, 255, 255, 0.14); + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.035)), + rgba(20, 20, 21, 0.92); + box-shadow: -28px 0 90px -34px rgba(0, 0, 0, 0.92); + backdrop-filter: blur(22px); + transform: translateX(100%); + transition: transform 220ms ease; +} + +.gallery-drawer.is-open { + transform: translateX(0); } .result-grid { @@ -404,13 +436,9 @@ input, textarea { @media (min-width: 1760px) { .session-split { - grid-template-columns: minmax(0, 1fr) minmax(400px, 480px); + grid-template-columns: minmax(0, 1fr) 84px; gap: 22px; } - - .session-pack-pane { - padding-left: 22px; - } } @media (max-width: 1180px) { @@ -424,16 +452,25 @@ input, textarea { flex-direction: column; } - .session-image-pane, - .session-pack-pane { - overflow: visible; + .gallery-rail { + min-height: 96px; + flex-direction: row; + overflow-x: auto; } - .session-pack-pane { - border-left: 0; - border-top: 1px solid rgba(255, 255, 255, 0.08); - padding-left: 0; - padding-top: 18px; + .gallery-thumb-list { + flex-direction: row; + overflow-x: auto; + overflow-y: hidden; + } + + .gallery-rail button { + width: 64px; + flex-shrink: 0; + } + + .gallery-drawer { + width: min(100vw, 720px); } } diff --git a/src/app/page.tsx b/src/app/page.tsx index 82b2bba..9273e3c 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,9 +2,9 @@ import { useCallback, useEffect, useState } from 'react'; import PromptPanel from '@/components/PromptPanel'; -import ResultGrid from '@/components/ResultGrid'; import Sidebar from '@/components/Sidebar'; import PackPanel from '@/components/PackPanel'; +import ProjectGalleryDrawer from '@/components/ProjectGalleryDrawer'; import { OasisCanvas } from '@/components/login/OasisCanvas'; import type { GenImage, @@ -311,14 +311,6 @@ export default function Home() {
{current && ( <> - - 图库 -
- Step · 02 · Quick Screen -

本次生成

+ Project Workspace +

项目工作台

{new Date(current.createdAt).toLocaleString('zh-CN')}

@@ -365,9 +357,6 @@ export default function Home() { {current.id}
-
- -
+
)} diff --git a/src/components/ProjectGalleryDrawer.tsx b/src/components/ProjectGalleryDrawer.tsx new file mode 100644 index 0000000..ee4e9af --- /dev/null +++ b/src/components/ProjectGalleryDrawer.tsx @@ -0,0 +1,117 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; +import type { GenSession } from '@/lib/types'; +import ResultGrid from './ResultGrid'; + +export type ProjectGalleryDrawerProps = { + session: GenSession; + onAction: (imageId: string, action: 'select' | 'reject' | 'reset') => void; +}; + +function statusClass(status: string) { + if (status === 'selected') return 'ring-[#e6f578]/80 shadow-[0_0_24px_-12px_rgba(230,245,120,0.9)]'; + if (status === 'rejected') return 'opacity-45 grayscale ring-white/10'; + return 'ring-white/12 hover:ring-white/28'; +} + +export default function ProjectGalleryDrawer({ session, onAction }: ProjectGalleryDrawerProps) { + const [open, setOpen] = useState(false); + const [mounted, setMounted] = useState(false); + const selectedCount = session.images.filter(image => image.status === 'selected').length; + + useEffect(() => { + setMounted(true); + }, []); + + useEffect(() => { + if (!open) return; + function handleKey(event: KeyboardEvent) { + if (event.key === 'Escape') setOpen(false); + } + window.addEventListener('keydown', handleKey); + return () => window.removeEventListener('keydown', handleKey); + }, [open]); + + const drawer = ( + <> +
setOpen(false)} /> + + + ); + + return ( + <> + + + {mounted ? createPortal(drawer, document.body) : null} + + ); +}