auto-save 2026-05-19 00:51 (~6)

This commit is contained in:
2026-05-19 00:51:15 +08:00
parent 54f47e33e0
commit 79b519eee9
6 changed files with 286 additions and 7 deletions

View File

@@ -259,6 +259,13 @@
"type": "session-heartbeat",
"message": "Codex 会话活跃 · 最近命令codex · 分支 master · 5 项未提交变更 · 最近提交auto-save 2026-05-19 00:40 (~4)",
"files_changed": 5
},
{
"ts": "2026-05-19T00:45:51+08:00",
"type": "commit",
"message": "auto-save 2026-05-19 00:45 (~5)",
"hash": "54f47e3",
"files_changed": 5
}
]
}

View File

@@ -7,6 +7,7 @@ import {
PACK_TEMPLATES,
TEMPLATE_FREEZE_VERSION,
TEMPLATE_SLOT_SUMMARY,
TEXT_TEMPLATES,
VIDEO_TEMPLATES,
} from '@/lib/templates';
@@ -26,6 +27,7 @@ export async function GET() {
requiredCount: PACK_TEMPLATES[kind].filter(template => template.required).length,
totalCount: PACK_TEMPLATES[kind].length,
})),
texts: TEXT_TEMPLATES,
videos: VIDEO_TEMPLATES,
});
}

View File

@@ -2,7 +2,7 @@
import { useState } from 'react';
import type { AssetPack, AssetTemplate, GenImage, GenSession, PackKind, ToyAsset } from '@/lib/types';
import { PACK_LABELS, PACK_ORDER, PACK_TEMPLATES, TEMPLATE_SLOT_SUMMARY, VIDEO_TEMPLATES } from '@/lib/templates';
import { PACK_LABELS, PACK_ORDER, PACK_TEMPLATES, TEMPLATE_SLOT_SUMMARY, TEXT_TEMPLATES, VIDEO_TEMPLATES } from '@/lib/templates';
const PACK_DESCRIPTIONS: Record<PackKind, string> = {
patent: '六面视图、45° 立体图、局部放大——外观专利素材',
@@ -289,14 +289,15 @@ function VideoSection({
function SlotOverview() {
const stats = [
{ label: '总预留位', value: TEMPLATE_SLOT_SUMMARY.totalReservedSlotCount },
{ label: '图片/脚本资产位', value: TEMPLATE_SLOT_SUMMARY.imageAssetCount },
{ label: '设计说明文字位', value: TEMPLATE_SLOT_SUMMARY.textAssetCount },
{ label: '必备图位', value: TEMPLATE_SLOT_SUMMARY.requiredImageAssetCount },
{ label: '可选扩展位', value: TEMPLATE_SLOT_SUMMARY.optionalImageAssetCount },
{ label: 'Seedance 视频任务', value: TEMPLATE_SLOT_SUMMARY.videoTaskCount },
];
return (
<div className="grid grid-cols-4 gap-2">
<div className="grid grid-cols-5 gap-2">
{stats.map(item => (
<div key={item.label} className="rounded-2xl bg-white/[0.035] ring-1 ring-white/[0.08] p-3">
<div className="text-lg font-semibold text-white font-mono">{item.value}</div>
@@ -307,6 +308,70 @@ function SlotOverview() {
);
}
function TextTemplateSection() {
const [showPromptId, setShowPromptId] = useState<string | null>(null);
return (
<section className="card p-5 space-y-4">
<div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-3">
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-sky-500 to-cyan-400 flex items-center justify-center text-white text-xs font-bold">T</div>
<div>
<div className="flex items-center gap-2">
<h3 className="text-base font-semibold text-white"></h3>
<span className="text-[11px] text-white/40">· {TEXT_TEMPLATES.length} </span>
</div>
<p className="text-[11px] text-white/45 mt-0.5"></p>
</div>
</div>
<span className="chip bg-sky-500/15 text-sky-200 border-sky-400/30">GPT Text</span>
</div>
<div className="space-y-2">
{TEXT_TEMPLATES.map(template => {
const open = showPromptId === template.id;
return (
<div key={template.id} className="grid grid-cols-[88px_1fr_120px] gap-4 p-3 rounded-2xl bg-white/[0.025] ring-1 ring-white/[0.05] hover:ring-white/[0.12] transition-all">
<div className="aspect-square rounded-xl bg-gradient-to-br from-sky-500/20 to-cyan-500/20 ring-1 ring-sky-400/20 flex flex-col items-center justify-center text-sky-200 text-[10px] font-mono">
<span>text</span>
{template.required && <span className="mt-1 text-[8px] text-white/70">required</span>}
</div>
<div className="min-w-0 space-y-1.5">
<div className="flex items-center gap-2 flex-wrap">
<span className="text-sm font-semibold text-white truncate">{template.title}</span>
<span className="chip bg-sky-500/15 text-sky-200 border-sky-400/30 text-[10px] py-0.5">{template.kind}</span>
<span className="chip chip-neutral text-[10px] py-0.5">{template.outputFormat}</span>
{template.required && <span className="chip chip-live text-[10px] py-0.5"></span>}
</div>
<p className="text-[11px] text-white/55 leading-relaxed line-clamp-2">{template.description}</p>
<button
onClick={() => setShowPromptId(open ? null : template.id)}
className="text-[10px] text-white/40 hover:text-sky-300 transition-colors flex items-center gap-1"
>
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<path d={open ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" />
</svg>
{open ? '收起 Prompt' : '查看 Prompt'}
</button>
{open && (
<pre className="mt-1.5 p-2.5 text-[10px] text-white/65 bg-black/40 rounded-lg ring-1 ring-white/[0.08] font-mono leading-relaxed whitespace-pre-wrap break-all max-h-32 overflow-y-auto">
{template.promptTemplate}
</pre>
)}
</div>
<div className="flex flex-col items-end justify-between gap-1.5 text-right">
<div className="text-[10px] font-mono text-white/55">{template.filenamePart}</div>
<div className="text-[9px] text-white/30 uppercase tracking-wider">{template.outputFormat}</div>
<div className="text-[10px] text-white/35"></div>
</div>
</div>
);
})}
</div>
</section>
);
}
export default function PackPanel({
session,
loadingKind,
@@ -437,6 +502,8 @@ export default function PackPanel({
);
})}
<TextTemplateSection />
{/* Video Section */}
<VideoSection
videoLoading={videoLoading}

View File

@@ -1,4 +1,4 @@
import type { AssetTemplate, CharacterSpec, PackKind } from './types';
import type { AssetTemplate, CharacterSpec, PackKind, TextTemplate } from './types';
export const FILENAME_SCHEMA = '{sessionId}_{characterSlug}_{pack}_{view}_{version}.{ext}';
export const TEMPLATE_FREEZE_VERSION = 'toy-pack-templates-v01';
@@ -7,9 +7,10 @@ export const PACK_LABELS: Record<PackKind, string> = {
patent: '专利包',
production: '生产打样包',
marketing: '宣发包',
accessories: '配件包',
};
export const PACK_ORDER: PackKind[] = ['patent', 'production', 'marketing'];
export const PACK_ORDER: PackKind[] = ['patent', 'accessories', 'production', 'marketing'];
export const VIDEO_TEMPLATES = [
{
@@ -95,6 +96,192 @@ const marketingChecklist = [
'镜头、色温、背景质感与同包素材统一',
];
const textChecklist = [
'围绕锁定 CharacterSpec不改核心外观设定',
'文字可直接复制给专利代理、工厂或宣发同事',
'不要编造真实商标、认证编号、价格和不存在的法律结论',
'保留待人工确认的尺寸、材料、色号或合规项',
];
export const TEXT_TEMPLATES: TextTemplate[] = [
{
id: 'text_project_design_brief',
kind: 'project',
title: '项目设计总说明',
description: '概括玩具定位、核心外观、目标用户和三类输出包用途。',
required: true,
outputFormat: 'paragraph',
filenamePart: 'project-design-brief',
promptTemplate: '根据角色设定生成项目设计总说明:{character}. 说明产品定位、目标用户、整体外观、材料倾向、专利/生产/宣发三类用途,中文 300-500 字。',
checklist: textChecklist,
},
{
id: 'text_character_setting',
kind: 'project',
title: '角色设定说明',
description: 'IP 名称、性格、故事背景、识别元素和禁忌项。',
required: true,
outputFormat: 'bullets',
filenamePart: 'character-setting',
promptTemplate: '根据角色设定生成 IP 角色设定说明:{character}. 输出名称、性格、故事背景、3-5 个识别元素、禁忌项和后续生成一致性要求。',
checklist: textChecklist,
},
{
id: 'text_patent_product_name',
kind: 'patent',
title: '外观设计产品名称',
description: '用于专利申请的产品名称建议。',
required: true,
outputFormat: 'paragraph',
filenamePart: 'patent-product-name',
promptTemplate: '根据角色设定生成外观设计专利产品名称建议:{character}. 名称应简洁、客观、避免营销词,给出 3 个候选和推荐理由。',
checklist: textChecklist,
},
{
id: 'text_patent_product_use',
kind: 'patent',
title: '产品用途说明',
description: '外观设计专利“产品用途”字段草稿。',
required: true,
outputFormat: 'paragraph',
filenamePart: 'patent-product-use',
promptTemplate: '根据角色设定生成外观设计专利产品用途说明:{character}. 用客观语言说明该产品用于玩具、摆件、陪伴礼品或收藏展示等用途,中文 80-150 字。',
checklist: textChecklist,
},
{
id: 'text_patent_design_points',
kind: 'patent',
title: '设计要点说明',
description: '外观设计专利“设计要点”字段草稿。',
required: true,
outputFormat: 'paragraph',
filenamePart: 'patent-design-points',
promptTemplate: '根据角色设定生成外观设计专利设计要点:{character}. 聚焦形状、图案、色彩或其结合,说明最应保护的外观特征,避免功能性表述,中文 100-200 字。',
checklist: textChecklist,
},
{
id: 'text_patent_representative_view',
kind: 'patent',
title: '最能表明设计要点的图片',
description: '建议指定主视图或立体图作为代表图。',
required: true,
outputFormat: 'paragraph',
filenamePart: 'patent-representative-view',
promptTemplate: '根据角色设定和专利视图清单,生成“最能表明设计要点的图片”建议:{character}. 从主视图、前侧立体图、局部图中推荐一个,并说明理由。',
checklist: textChecklist,
},
{
id: 'text_patent_view_brief',
kind: 'patent',
title: '各视图简要说明',
description: '对应主视图、后视图、左右视图、俯仰视图、立体图和局部图。',
required: true,
outputFormat: 'table',
filenamePart: 'patent-view-brief',
promptTemplate: '根据角色设定生成外观设计专利各视图简要说明表:{character}. 覆盖主视图、后视图、左视图、右视图、俯视图、仰视图、前侧立体图、后侧立体图、局部放大图、使用状态图、成套产品图。',
checklist: textChecklist,
},
{
id: 'text_patent_color_claim',
kind: 'patent',
title: '色彩保护说明',
description: '是否请求保护色彩的说明建议。',
required: false,
outputFormat: 'paragraph',
filenamePart: 'patent-color-claim',
promptTemplate: '根据角色设定生成外观设计专利色彩保护建议:{character}. 判断是否建议请求保护色彩,说明理由,并标记需由专利代理最终确认。',
checklist: textChecklist,
},
{
id: 'text_production_brief',
kind: 'production',
title: '工厂打样总说明',
description: '给工厂的打样目标、尺寸、材料、结构和注意事项。',
required: true,
outputFormat: 'paragraph',
filenamePart: 'production-brief',
promptTemplate: '根据角色设定生成工厂打样总说明:{character}. 说明目标尺寸档、整体形态、材料倾向、软硬度、配件固定、缝线和待确认事项,中文 300-500 字。',
checklist: textChecklist,
},
{
id: 'text_production_cmf',
kind: 'production',
title: 'CMF 材料颜色工艺说明',
description: '颜色、材料、表面工艺、刺绣/印花和填充说明。',
required: true,
outputFormat: 'table',
filenamePart: 'production-cmf',
promptTemplate: '根据角色设定生成 CMF 表格:{character}. 列出部位、建议材料、颜色/色号占位、工艺、风险和待确认项。',
checklist: textChecklist,
},
{
id: 'text_production_bom',
kind: 'production',
title: 'BOM 部件清单',
description: '头、身体、四肢、耳朵、衣服、配件、包装等部件清单。',
required: true,
outputFormat: 'table',
filenamePart: 'production-bom',
promptTemplate: '根据角色设定生成毛绒玩具 BOM 部件清单:{character}. 包含部件名称、数量、材料、工艺、尺寸占位、供应/打样注意事项。',
checklist: textChecklist,
},
{
id: 'text_production_qc',
kind: 'production',
title: '打样验收标准',
description: '尺寸偏差、颜色、刺绣、缝线、配件牢固度和安全提醒。',
required: true,
outputFormat: 'bullets',
filenamePart: 'production-qc',
promptTemplate: '根据角色设定生成工厂打样验收标准:{character}. 按尺寸、外观一致性、颜色、材料、刺绣印花、缝线、配件固定、包装和安全风险列出检查项。',
checklist: textChecklist,
},
{
id: 'text_marketing_core_copy',
kind: 'marketing',
title: '核心卖点文案',
description: '主标题、副标题、3-5 个卖点和短描述。',
required: true,
outputFormat: 'bullets',
filenamePart: 'marketing-core-copy',
promptTemplate: '根据角色设定生成宣发核心文案:{character}. 输出主标题 5 个、副标题 5 个、3-5 个卖点、短描述 3 版,适合电商和社媒。',
checklist: textChecklist,
},
{
id: 'text_marketing_detail_page',
kind: 'marketing',
title: '电商详情页文字结构',
description: '详情页模块标题、正文、卖点、尺寸、材料和包装说明。',
required: true,
outputFormat: 'table',
filenamePart: 'marketing-detail-page',
promptTemplate: '根据角色设定生成电商详情页文字结构:{character}. 按模块输出标题、正文、配图建议、卖点、尺寸/材料/包装说明,中文。',
checklist: textChecklist,
},
{
id: 'text_marketing_social_posts',
kind: 'marketing',
title: '社媒发布文案',
description: '小红书、朋友圈、短视频标题和标签。',
required: false,
outputFormat: 'bullets',
filenamePart: 'marketing-social-posts',
promptTemplate: '根据角色设定生成社媒发布文案:{character}. 输出小红书标题 10 个、正文 3 版、短视频标题 10 个、标签建议,避免夸大宣传。',
checklist: textChecklist,
},
{
id: 'text_video_script_pack',
kind: 'video',
title: '视频脚本文字包',
description: '旋转展示、开箱、触感、角色故事、工厂预览的旁白和字幕。',
required: false,
outputFormat: 'script',
filenamePart: 'video-script-pack',
promptTemplate: '根据角色设定生成视频脚本文字包:{character}. 覆盖 360 旋转、开箱、触感细节、角色故事、工厂预览,每条包含镜头、字幕、旁白、时长和画面说明。',
checklist: textChecklist,
},
];
export const PATENT_TEMPLATES: AssetTemplate[] = [
{
id: 'patent_front',
@@ -735,8 +922,12 @@ export const TEMPLATE_SLOT_SUMMARY = {
imageAssetCount: PACK_TEMPLATE_SUMMARY.reduce((sum, pack) => sum + pack.totalCount, 0),
requiredImageAssetCount: PACK_TEMPLATE_SUMMARY.reduce((sum, pack) => sum + pack.requiredCount, 0),
optionalImageAssetCount: PACK_TEMPLATE_SUMMARY.reduce((sum, pack) => sum + pack.optionalCount, 0),
textAssetCount: TEXT_TEMPLATES.length,
requiredTextAssetCount: TEXT_TEMPLATES.filter(template => template.required).length,
optionalTextAssetCount: TEXT_TEMPLATES.filter(template => !template.required).length,
videoTaskCount: VIDEO_TEMPLATES.length,
storyboardAssetCount: MARKETING_TEMPLATES.filter(template => template.id.startsWith('video_')).length,
totalReservedSlotCount: PACK_TEMPLATE_SUMMARY.reduce((sum, pack) => sum + pack.totalCount, 0) + TEXT_TEMPLATES.length + VIDEO_TEMPLATES.length,
packSummary: PACK_TEMPLATE_SUMMARY,
};

View File

@@ -31,7 +31,7 @@ export type GenerateResponse = {
provider: 'mock' | 'gpt';
};
export type PackKind = 'patent' | 'production' | 'marketing';
export type PackKind = 'patent' | 'production' | 'marketing' | 'accessories';
export type AssetStatus = 'draft' | 'selected' | 'needs_regen' | 'approved' | 'exported';
@@ -68,6 +68,18 @@ export type AssetTemplate = {
checklist: string[];
};
export type TextTemplate = {
id: string;
kind: PackKind | 'video' | 'project';
title: string;
description: string;
required: boolean;
outputFormat: 'paragraph' | 'bullets' | 'table' | 'script' | 'json';
filenamePart: string;
promptTemplate: string;
checklist: string[];
};
export type ToyAsset = {
id: string;
templateId: string;

File diff suppressed because one or more lines are too long