diff --git a/.memory/worklog.json b/.memory/worklog.json
index 41adf0f..b87755b 100644
--- a/.memory/worklog.json
+++ b/.memory/worklog.json
@@ -1,71 +1,90 @@
{
- "entries" : [
+ "entries": [
{
- "files_changed" : 0,
- "hash" : "",
- "message" : "项目创建: AI玩具专利生成工作流",
- "ts" : "2026-05-18T00:00:00+08:00",
- "type" : "milestone"
+ "files_changed": 0,
+ "hash": "",
+ "message": "项目创建: AI玩具专利生成工作流",
+ "ts": "2026-05-18T00:00:00+08:00",
+ "type": "milestone"
},
{
- "files_changed" : 6,
- "hash" : "5e4c6e5",
- "message" : "init: project scaffold",
- "ts" : "2026-05-18T10:35:02+08:00",
- "type" : "commit"
+ "files_changed": 6,
+ "hash": "5e4c6e5",
+ "message": "init: project scaffold",
+ "ts": "2026-05-18T10:35:02+08:00",
+ "type": "commit"
},
{
- "files_changed" : 4,
- "hash" : "0accb73",
- "message" : "auto-save 2026-05-18 10:39 (+1, ~1)",
- "ts" : "2026-05-18T10:39:25+08:00",
- "type" : "commit"
+ "files_changed": 4,
+ "hash": "0accb73",
+ "message": "auto-save 2026-05-18 10:39 (+1, ~1)",
+ "ts": "2026-05-18T10:39:25+08:00",
+ "type": "commit"
},
{
- "files_changed" : 20,
- "hash" : "494779d",
- "message" : "auto-save 2026-05-18 10:44 (+6, ~2)",
- "ts" : "2026-05-18T10:46:21+08:00",
- "type" : "commit"
+ "files_changed": 20,
+ "hash": "494779d",
+ "message": "auto-save 2026-05-18 10:44 (+6, ~2)",
+ "ts": "2026-05-18T10:46:21+08:00",
+ "type": "commit"
},
{
- "files_changed" : 4,
- "hash" : "aa5cac1",
- "message" : "auto-save 2026-05-18 10:53 (+2, ~2)",
- "ts" : "2026-05-18T10:53:53+08:00",
- "type" : "commit"
+ "files_changed": 4,
+ "hash": "aa5cac1",
+ "message": "auto-save 2026-05-18 10:53 (+2, ~2)",
+ "ts": "2026-05-18T10:53:53+08:00",
+ "type": "commit"
},
{
- "files_changed" : 3,
- "hash" : "8e1147c",
- "message" : "feat: MVP 跑通 — prompt → 批量生成 → 九宫格快筛 → 选中落盘",
- "ts" : "2026-05-18T11:07:53+08:00",
- "type" : "commit"
+ "files_changed": 3,
+ "hash": "8e1147c",
+ "message": "feat: MVP 跑通 — prompt → 批量生成 → 九宫格快筛 → 选中落盘",
+ "ts": "2026-05-18T11:07:53+08:00",
+ "type": "commit"
},
{
- "files_changed" : 1,
- "message" : "启动 Codex 接力会话 · 已载入 Claude 最近会话,等待下一条指令 · 分支 master · 1 项未提交变更 · 最近提交:feat: MVP 跑通 — prompt → 批量生成 → 九宫格快筛 → 选中落盘",
- "ts" : "2026-05-18T15:13:48Z",
- "type" : "assistant-session"
+ "files_changed": 1,
+ "message": "启动 Codex 接力会话 · 已载入 Claude 最近会话,等待下一条指令 · 分支 master · 1 项未提交变更 · 最近提交:feat: MVP 跑通 — prompt → 批量生成 → 九宫格快筛 → 选中落盘",
+ "ts": "2026-05-18T15:13:48Z",
+ "type": "assistant-session"
},
{
- "files_changed" : 1,
- "message" : "启动 Cursor 接力会话 · 已载入 Claude 最近会话,等待下一条指令 · 分支 master · 1 项未提交变更 · 最近提交:feat: MVP 跑通 — prompt → 批量生成 → 九宫格快筛 → 选中落盘",
- "ts" : "2026-05-18T15:14:10Z",
- "type" : "assistant-session"
+ "files_changed": 1,
+ "message": "启动 Cursor 接力会话 · 已载入 Claude 最近会话,等待下一条指令 · 分支 master · 1 项未提交变更 · 最近提交:feat: MVP 跑通 — prompt → 批量生成 → 九宫格快筛 → 选中落盘",
+ "ts": "2026-05-18T15:14:10Z",
+ "type": "assistant-session"
},
{
- "files_changed" : 2,
- "hash" : "e3555da",
- "message" : "auto-save 2026-05-18 23:15 (+1, ~1)",
- "ts" : "2026-05-18T23:15:09+08:00",
- "type" : "commit"
+ "files_changed": 2,
+ "hash": "e3555da",
+ "message": "auto-save 2026-05-18 23:15 (+1, ~1)",
+ "ts": "2026-05-18T23:15:09+08:00",
+ "type": "commit"
},
{
- "files_changed" : 2,
- "message" : "启动 Claude 接力会话 · 已载入 Claude / Codex 最近会话,等待下一条指令 · 分支 master · 2 项未提交变更 · 最近提交:auto-save 2026-05-18 23:15 (+1, ~1)",
- "ts" : "2026-05-18T15:16:48Z",
- "type" : "assistant-session"
+ "files_changed": 2,
+ "message": "启动 Claude 接力会话 · 已载入 Claude / Codex 最近会话,等待下一条指令 · 分支 master · 2 项未提交变更 · 最近提交:auto-save 2026-05-18 23:15 (+1, ~1)",
+ "ts": "2026-05-18T15:16:48Z",
+ "type": "assistant-session"
+ },
+ {
+ "ts": "2026-05-18T23:22:17+08:00",
+ "type": "commit",
+ "message": "auto-save 2026-05-18 23:20 (~3)",
+ "hash": "446e012",
+ "files_changed": 3
+ },
+ {
+ "ts": "2026-05-18T15:23:50Z",
+ "type": "session-heartbeat",
+ "message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 4 项未提交变更 · 最近提交:auto-save 2026-05-18 23:20 (~3)",
+ "files_changed": 4
+ },
+ {
+ "ts": "2026-05-18T15:26:50Z",
+ "type": "session-heartbeat",
+ "message": "Claude 会话活跃 · 最近命令:claude · 分支 master · 6 项未提交变更 · 最近提交:auto-save 2026-05-18 23:20 (~3)",
+ "files_changed": 6
}
]
}
diff --git a/.playwright-mcp/page-2026-05-18T15-28-17-188Z.yml b/.playwright-mcp/page-2026-05-18T15-28-17-188Z.yml
new file mode 100644
index 0000000..d9e7e49
--- /dev/null
+++ b/.playwright-mcp/page-2026-05-18T15-28-17-188Z.yml
@@ -0,0 +1,51 @@
+- generic [active] [ref=e1]:
+ - generic [ref=e2]:
+ - complementary [ref=e3]:
+ - generic [ref=e4]:
+ - button "新会话" [ref=e5] [cursor=pointer]:
+ - img [ref=e6]
+ - text: 新会话
+ - button "收起侧栏" [ref=e8] [cursor=pointer]:
+ - img [ref=e9]
+ - generic [ref=e11]: 最近
+ - generic [ref=e13]: 还没有生成记录
+ - main [ref=e14]:
+ - generic [ref=e15]:
+ - generic [ref=e16]:
+ - generic [ref=e17]:
+ - heading "AI 玩具专利生成工作流" [level=1] [ref=e18]
+ - paragraph [ref=e19]: 批量出意向 → 快筛 → 多角度尺寸 → 喂专利
+ - generic [ref=e21]: "?"
+ - generic [ref=e24]:
+ - generic [ref=e25]:
+ - generic [ref=e26]: Prompt
+ - textbox "描述要生成的玩具意向…" [ref=e27]: AI 毛绒陪伴玩具,机甲头盔,胸前挂 M logo,橙白配色,圆胖体型
+ - paragraph [ref=e28]:
+ - text: 按
+ - generic [ref=e29]: ⌘
+ - generic [ref=e30]: ↵
+ - text: 提交
+ - generic [ref=e31]:
+ - generic [ref=e32]: 参考图 · 可选,最多 4 张
+ - button "+" [ref=e34] [cursor=pointer]
+ - generic [ref=e35]:
+ - generic [ref=e36]: 风格
+ - generic [ref=e37]:
+ - button "无" [ref=e38] [cursor=pointer]
+ - button "毛绒玩偶" [ref=e39] [cursor=pointer]
+ - button "机甲风" [ref=e40] [cursor=pointer]
+ - button "可爱萌系" [ref=e41] [cursor=pointer]
+ - button "专利蓝图" [ref=e42] [cursor=pointer]
+ - button "赛博朋克" [ref=e43] [cursor=pointer]
+ - button "极简" [ref=e44] [cursor=pointer]
+ - generic [ref=e45]:
+ - generic [ref=e46]:
+ - generic [ref=e47]: 数量
+ - generic [ref=e48]:
+ - button "4 张" [ref=e49] [cursor=pointer]
+ - button "8 张" [ref=e50] [cursor=pointer]
+ - button "12 张" [ref=e51] [cursor=pointer]
+ - button "批量生成" [ref=e52] [cursor=pointer]:
+ - text: 批量生成
+ - img [ref=e53]
+ - alert [ref=e55]
\ No newline at end of file
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index f525d90..2e4057a 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -9,7 +9,7 @@ export const metadata: Metadata = {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
-
{children}
+ {children}
);
}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index faed549..364a3f8 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -11,6 +11,7 @@ export default function Home() {
const [current, setCurrent] = useState(null);
const [loading, setLoading] = useState(false);
const [provider, setProvider] = useState('?');
+ const [sidebarOpen, setSidebarOpen] = useState(true);
const refreshSessions = useCallback(async () => {
const r = await fetch('/api/sessions');
@@ -60,36 +61,51 @@ export default function Home() {
}
return (
-
+
setSidebarOpen(v => !v)}
sessions={sessions}
currentId={current?.id ?? null}
onPick={id => setCurrent(sessions.find(s => s.id === id) ?? null)}
onNew={() => setCurrent(null)}
/>
-
+
+
-
-
- {current && (
-
-
-
本次生成 · {new Date(current.createdAt).toLocaleString('zh-CN')}
- {current.id}
-
-
-
- )}
+
+
+ {current && (
+
+
+
+
本次生成
+
+ {new Date(current.createdAt).toLocaleString('zh-CN')}
+
+
+
{current.id}
+
+
+
+ )}
+
diff --git a/src/components/PromptPanel.tsx b/src/components/PromptPanel.tsx
index 152197b..878b333 100644
--- a/src/components/PromptPanel.tsx
+++ b/src/components/PromptPanel.tsx
@@ -38,35 +38,42 @@ export default function PromptPanel({ onGenerate, loading }: PromptPanelProps) {
}
return (
-
+
-
-
+
+
{refs.map((r, i) => (
-
+
))}
{refs.length < 4 && (
)}
-
-
+
+
{PRESET_STYLES.map(s => (
))}
-
+
-
-
+
+
{[4, 8, 12].map(n => (
+ className={`seg-item ${count === n ? 'seg-item-active' : ''}`}
+ >{n} 张
))}
diff --git a/src/components/ResultGrid.tsx b/src/components/ResultGrid.tsx
index 3d8d62c..cb2478c 100644
--- a/src/components/ResultGrid.tsx
+++ b/src/components/ResultGrid.tsx
@@ -9,9 +9,10 @@ export type ResultGridProps = {
};
export default function ResultGrid({ images, onAction }: ResultGridProps) {
- // 键盘:1-9 切换选中,shift+1-9 打叉
useEffect(() => {
function handler(e: KeyboardEvent) {
+ const target = e.target as HTMLElement;
+ if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA')) return;
const n = parseInt(e.key, 10);
if (isNaN(n) || n < 1 || n > 9 || n > images.length) return;
const img = images[n - 1];
@@ -26,34 +27,55 @@ export default function ResultGrid({ images, onAction }: ResultGridProps) {
}, [images, onAction]);
const cols = images.length <= 4 ? 'grid-cols-2' : images.length <= 9 ? 'grid-cols-3' : 'grid-cols-4';
+ const selectedCount = images.filter(i => i.status === 'selected').length;
return (
-
-
-
- 快捷键:数字键 1-{Math.min(9, images.length)} 选中 / Shift+数字键 打叉
-
-
- 已选 {images.filter(i => i.status === 'selected').length} / {images.length}
-
+
+
+
+ 1
+ –
+ {Math.min(9, images.length)}
+ 选中
+ /
+ ⇧
+ +
+ 1
+ 打叉
+
+
+ 已选 {selectedCount}
+ / {images.length}
+
+
{images.map((img, i) => (
{i + 1}
-
+
+ {img.status === 'selected' && (
+
✓
+ )}
+ {img.status === 'rejected' && (
+
✕
+ )}
+
+
+ className="flex-1 px-3 py-1.5 rounded-lg bg-white text-zinc-900 text-xs font-medium hover:bg-zinc-100 transition-colors shadow-sm"
+ >
+ {img.status === 'selected' ? '✓ 已选' : '选中'}
+
+ className="px-3 py-1.5 rounded-lg bg-white/90 text-zinc-700 text-xs font-medium hover:bg-white hover:text-red-600 transition-colors shadow-sm"
+ >✕
))}
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
index 27a3488..fe841b3 100644
--- a/src/components/Sidebar.tsx
+++ b/src/components/Sidebar.tsx
@@ -3,39 +3,96 @@
import type { GenSession } from '@/lib/types';
export default function Sidebar({
+ open,
+ onToggle,
sessions,
currentId,
onPick,
onNew,
}: {
+ open: boolean;
+ onToggle: () => void;
sessions: GenSession[];
currentId: string | null;
onPick: (id: string) => void;
onNew: () => void;
}) {
+ if (!open) {
+ return (
+
+ );
+ }
+
return (
-