diff --git a/lib/prompts/novel-promotion/select_prop.en.txt b/lib/prompts/novel-promotion/select_prop.en.txt index c7a6434..29c2c55 100644 --- a/lib/prompts/novel-promotion/select_prop.en.txt +++ b/lib/prompts/novel-promotion/select_prop.en.txt @@ -2,6 +2,9 @@ You are a key story prop extractor. Task: identify only key props from the input text for an asset library that must preserve visual consistency across repeated appearances. Be conservative. Return JSON only. +Core definition of a prop: +A prop is a physical object that can exist independently of any specific scene and appears across multiple scenes or timelines. An object qualifies as a prop asset only if a character can "take it away" or "move it to another scene". Most stories have very few props, or even none at all. + Output format: { "props": [ @@ -14,11 +17,11 @@ Output format: Key prop criteria: 1. It must be a real physical object that actually appears in the story. -2. It must serve a clear story function rather than being background dressing. -3. It must satisfy at least one of the following: +2. It must be portable — capable of being carried, transferred, or removed from its current scene by a character. +3. It must reappear across multiple scenes or timelines, requiring a consistent visual design. +4. It must satisfy at least one of the following: - characters hold it, use it, fight over it, deliver it, hide it, lose it, or search for it - it is a key tool, weapon, artifact, piece of evidence, token, key, or clue carrier - - it is likely to reappear and therefore needs a consistent visual design - removing it would materially weaken plot comprehension or a key action Strictly exclude: @@ -27,6 +30,8 @@ Strictly exclude: 3. Environmental elements that belong to the scene unless they are explicitly used as key props. 4. Ordinary clothing, makeup, and accessories unless they are themselves key clues or tokens. 5. Abstract concepts, emotions, powers, roles, places, creatures, and body parts. +6. Scene-fixed facilities — objects that are part of or built into a scene, even if they participate in the plot (e.g. a hacked computer, a smashed window, a fireplace on fire). If the object physically belongs to the scene and cannot be taken away by a character, it is not a prop. These are "scene states" and should be handled by scene descriptions. +7. Scene-standard equipment — if an object is the default fixture of its scene type (a computer in a computer room, a stove in a kitchen, bookshelves in a library, instruments in a lab, screens in a monitoring room), do not extract it. Decision bias: 1. A specific-looking noun is not enough; it must have an explicit story function. @@ -34,6 +39,18 @@ Decision bias: 3. If it merely appears but is not used, emphasized, or plot-relevant, do not output it. 4. If you are unsure whether it deserves an asset entry, do not output it. 5. Prefer under-extraction. Never output props just to increase the count. +6. Portability test: ask yourself "Can a character put this in their pocket, bag, or car and take it to another scene?" If not, do not output it. + +Example judgements (to calibrate your standard): +✅ Extract: a revolver the character carries at all times (cross-scene, portable) +✅ Extract: an evidence envelope (discovered, handed over, appears in multiple scenes) +✅ Extract: a time-manipulating watch worn by the protagonist (core prop, present throughout) +✅ Extract: a black SUV driven by the protagonist (cross-scene transport) +❌ Skip: a computer in a computer room (scene-fixed facility) +❌ Skip: a hacked computer displaying key clues (state change of a scene facility, not portable) +❌ Skip: a surveillance monitor in a monitoring room (scene-fixed facility) +❌ Skip: a refrigerator in a kitchen (scene-standard equipment) +❌ Skip: a rare book in a library (unless the character takes it away and uses it in another scene) Output rules: 1. Only output `name` and `summary`. @@ -41,8 +58,8 @@ Output rules: 3. Do not repeat props that already exist in the prop library with the exact same name. 4. Keep names stable and short. 5. Keep summaries objective. -6. Usually output no more than 3-5 props unless more are clearly all key props. -7. If none exist, return {"props": []}. +6. Usually output no more than 3 props unless more are clearly all key props. +7. If none exist, return {"props": []}. Returning an empty array is correct in most cases. 8. Replace raw quotation marks inside JSON string values with corner brackets「」. Input: diff --git a/lib/prompts/novel-promotion/select_prop.zh.txt b/lib/prompts/novel-promotion/select_prop.zh.txt index 157c92f..a39b3d0 100644 --- a/lib/prompts/novel-promotion/select_prop.zh.txt +++ b/lib/prompts/novel-promotion/select_prop.zh.txt @@ -1,7 +1,10 @@ -你是“关键剧情道具资产分析师”。 +你是"关键剧情道具资产分析师"。 任务:从输入文本中只识别【关键道具】,用于建立需要长期保持外观一致的资产库。宁缺毋滥。只返回 JSON,不得包含任何额外解释或 markdown。 +道具的核心定义: +道具是可以脱离特定场景独立存在的、跨场景/跨时间线出现的实体物件。一个物件必须能被角色「带走」或「转移到另一个场景」,才有资格成为道具资产。大部分故事中道具数量非常少,甚至为零。 + 输出格式: { "props": [ @@ -14,11 +17,11 @@ 关键道具判定标准: 1. 必须是剧情中真实出现的实体物件。 -2. 必须在剧情中承担明确功能,而不只是背景摆设。 -3. 必须至少满足以下一种情况: +2. 必须是可移动的——能够被角色携带、转移、带离当前场景。 +3. 必须跨场景或跨时间线重复出现,且需要保持外观一致。 +4. 必须至少满足以下一种情况: - 被角色持有、使用、争夺、交付、隐藏、丢失、寻找 - 是推进情节的关键工具、武器、法器、证物、信物、钥匙、线索载体 - - 后续大概率需要重复出镜,且需要保持外观一致 - 去掉它会明显影响剧情理解或关键动作成立 严格不提取: @@ -27,22 +30,36 @@ 3. 场景自带的环境元素,除非它被明确当作关键道具使用。 4. 普通服装、妆容、饰品,除非它本身就是关键线索或关键信物。 5. 抽象概念、情绪、能力、身份、地点、生物、身体部位。 +6. 场景固有设施——物件是某个场景的组成部分或内置设备,即便它参与了剧情互动(如被黑客入侵的电脑、被砸碎的窗户、着火的壁炉),只要它在物理上依附于场景、无法被角色带走,就不是道具。这类属于"场景状态",由场景描述承载。 +7. 场景常规配置——如果一个物件是该类场景的标配(电脑房的电脑、厨房的灶台、图书馆的书架、实验室的仪器、监控室的屏幕),直接不提取。 判断倾向: 1. 仅因外观具体、名词明确,不足以成为关键道具;必须有明确剧情作用。 2. 如果一个物件既可能是背景物,也可能是道具,默认按背景物处理,不输出。 -3. 如果只是“出现过”,但没有“被使用/被强调/影响剧情”,不输出。 +3. 如果只是"出现过",但没有"被使用/被强调/影响剧情",不输出。 4. 如果不确定它是否值得进入资产库,直接不输出。 5. 优先少报,禁止为了凑数量而输出。 +6. 可移动性测试:问自己"角色能把它装进口袋/背包/车里带到另一个场景吗?"如果不能,不输出。 + +示例判断(帮助校准标准): +✅ 应提取:角色随身携带的左轮手枪(跨场景出现、可移动) +✅ 应提取:关键证物信封(被发现、传递、多场景出现) +✅ 应提取:主角可操控时间的手表(核心道具,贯穿全剧) +✅ 应提取:主角驾驶的黑色越野车(跨场景移动工具) +❌ 不提取:电脑房里的电脑(场景固有设施) +❌ 不提取:被黑客入侵、显示关键线索的电脑(场景设施的状态变化,不可移动) +❌ 不提取:监控室的监控屏幕(场景固有设施) +❌ 不提取:厨房的冰箱(场景常规配置) +❌ 不提取:图书馆的某本古籍(除非角色将它取走带到其他场景使用) 输出要求: 1. 只输出两个字段:name、summary。 2. name 不能为空;summary 不能为空。 3. 如果道具库里已经有完全同名道具,不要重复输出。 -4. 名称尽量简洁稳定,例如“青铜匕首”“录音笔”“红绳手链”。 +4. 名称尽量简洁稳定,例如"青铜匕首""录音笔""红绳手链"。 5. summary 只写客观描述,不写剧情推断。 -6. 通常不超过 3-5 个;只有确实都是关键道具时才可更多。 -7. 如果没有合适道具,返回 {"props": []}。 +6. 通常不超过 3 个;只有确实都是关键道具时才可更多。 +7. 如果没有合适道具,返回 {"props": []}。绝大多数情况下返回空数组是正确的。 8. JSON 字符串值中的引号统一替换为「」。 输入文本: diff --git a/messages/en/configModal.json b/messages/en/configModal.json index f0f01c4..b7817fa 100644 --- a/messages/en/configModal.json +++ b/messages/en/configModal.json @@ -3,7 +3,8 @@ "subtitle": "Defaults to the global settings. You can customize models for this project only — changes apply to this project only.", "saved": "Saved", "autoSave": "Auto-save", - "visualStyle": "Visual Style", + "visualSettings": "Visual Settings", + "visualStyle": "Art Style", "modelParams": "Model Parameters", "aspectRatio": "Aspect Ratio", "ttsSettings": "TTS Settings", diff --git a/messages/en/home.json b/messages/en/home.json new file mode 100644 index 0000000..bc48079 --- /dev/null +++ b/messages/en/home.json @@ -0,0 +1,15 @@ +{ + "title": "Quick Start", + "subtitle": "Describe your story and let AI generate cinematic short dramas", + "inputPlaceholder": "Enter your story idea, novel excerpt, or script outline...", + "startCreation": "Start Creating", + "recentProjects": "Recent Projects", + "viewAll": "View All Projects", + "noProjects": "No projects yet. Start your first creation from above!", + "ago": { + "justNow": "Just now", + "minutesAgo": "{n}m ago", + "hoursAgo": "{n}h ago", + "daysAgo": "{n}d ago" + } +} diff --git a/messages/en/novel-promotion.json b/messages/en/novel-promotion.json index decc830..9ce454f 100644 --- a/messages/en/novel-promotion.json +++ b/messages/en/novel-promotion.json @@ -123,8 +123,8 @@ "5_4": "Horizontal · Banner", "21_9": "Ultra‑wide · Cinema feel" }, - "visualStyle": "Visual Style", - "visualStyleHint": "Pick a style that matches your audience — e.g. Realistic for live‑action, Anime for 2D content", + "visualStyle": "Art Style", + "visualStyleHint": "Choose an art style that fits your project — e.g. Realistic for live-action, Anime for 2D content", "currentConfigSummary": "Current config: {ratio} · {style}. All subsequent generations will use this combo.", "assetLibraryRatioNote": "Asset library ratios are not affected", "moreConfig": "For more configuration options, click the 「 Settings」 button in the top right", @@ -134,7 +134,16 @@ }, "creating": "AI Creating...", "ready": "✓ Configuration complete, ready for next step", - "pleaseInput": "Please enter script content first" + "pleaseInput": "Please enter script content first", + "longTextDetection": { + "title": "🚀 Smart Episode Splitting Recommended", + "description": "Detected ~{count} characters. Processing long text as a single episode may reduce output quality.", + "strongRecommend": "We strongly recommend using Smart Split. AI will automatically identify chapters, split into episodes, and process them in parallel for significantly better results.", + "continueAnyway": "Continue as single episode", + "smartSplit": "Smart Split", + "smartSplitRecommend": "Recommended", + "singleEpisodeWarning": "All content will be processed as one episode" + } }, "execution": { "selectEpisode": "Please select an episode first", diff --git a/messages/en/workspaceRedesign.json b/messages/en/workspaceRedesign.json new file mode 100644 index 0000000..c04c6b9 --- /dev/null +++ b/messages/en/workspaceRedesign.json @@ -0,0 +1,77 @@ +{ + "pageTitle": "Homepage Redesign Test", + "switchVariant": "Switch Layout", + "currentVariant": "Current Layout", + "inputPlaceholder": "Describe the story you want to create...", + "startCreation": "Start Creating", + "recentProjects": "Recent Projects", + "viewAll": "View All", + "noRecentProjects": "No recent projects", + "latestUpdate": "Latest Update", + "style": "Style", + "ratio": "Ratio", + "quality": "Quality", + "model": "Model", + "styles": { + "anime": "Anime", + "realistic": "Realistic", + "watercolor": "Watercolor", + "cyberpunk": "Cyberpunk", + "ghibli": "Ghibli", + "ink": "Ink Wash" + }, + "ratios": { + "r16_9": "16:9 Landscape", + "r9_16": "9:16 Portrait", + "r1_1": "1:1 Square", + "r4_3": "4:3 Classic" + }, + "qualities": { + "standard": "Standard", + "high": "High", + "ultra": "Ultra" + }, + "quickActions": { + "title": "Quick Start", + "fromNovel": "Import from Novel", + "fromScript": "Create from Script", + "fromScratch": "Start from Scratch", + "fromTemplate": "Use Template" + }, + "mockProject": { + "name1": "Campus Youth Story", + "desc1": "A romantic tale about high school life", + "name2": "Star Trek Journal", + "desc2": "A space adventure sci-fi short drama", + "name3": "Ancient Xianxia Chronicles", + "desc3": "Love and rivalry in a cultivation world", + "name4": "Urban Encounters", + "desc4": "Wondrous encounters in a modern city", + "name5": "The Last Travelers", + "desc5": "A survival journey in a post-apocalyptic world" + }, + "variantNames": { + "v1": "Grid Cards", + "v2": "Horizontal Scroll", + "v3": "Compact List", + "v4": "Featured First", + "v5": "Minimal List" + }, + "variantDescs": { + "v1": "Standard 5-column grid with system card style", + "v2": "Horizontal scrollable cards with snap", + "v3": "Single-row list with left-right info", + "v4": "Large first card + small card grid", + "v5": "Minimal dot-list matching input width" + }, + "episodes": "Episodes", + "images": "Images", + "videos": "Videos", + "updated": "Updated", + "ago": { + "justNow": "Just now", + "minutesAgo": "{n}m ago", + "hoursAgo": "{n}h ago", + "daysAgo": "{n}d ago" + } +} diff --git a/messages/zh/configModal.json b/messages/zh/configModal.json index 5cc1ee9..f2d77b5 100644 --- a/messages/zh/configModal.json +++ b/messages/zh/configModal.json @@ -3,7 +3,8 @@ "subtitle": "默认沿用设置中心的全局配置,也可为当前项目单独自定义,修改仅对本项目生效。", "saved": "已保存", "autoSave": "自动保存", - "visualStyle": "视觉风格", + "visualSettings": "画面设置", + "visualStyle": "画面风格", "modelParams": "模型参数", "aspectRatio": "画面比例", "ttsSettings": "旁白配置", diff --git a/messages/zh/home.json b/messages/zh/home.json new file mode 100644 index 0000000..71dbab1 --- /dev/null +++ b/messages/zh/home.json @@ -0,0 +1,15 @@ +{ + "title": "快速开始", + "subtitle": "描述你想要创作的故事,AI 为你智能生成影视短剧", + "inputPlaceholder": "输入你的故事创意、小说片段或剧本大纲...", + "startCreation": "开始创作", + "recentProjects": "最近项目", + "viewAll": "查看全部项目", + "noProjects": "还没有项目,从上方开始你的第一个创作吧", + "ago": { + "justNow": "刚刚", + "minutesAgo": "{n}分钟前", + "hoursAgo": "{n}小时前", + "daysAgo": "{n}天前" + } +} diff --git a/messages/zh/novel-promotion.json b/messages/zh/novel-promotion.json index 9354e4a..e86ab73 100644 --- a/messages/zh/novel-promotion.json +++ b/messages/zh/novel-promotion.json @@ -123,8 +123,8 @@ "5_4": "横屏 · Banner", "21_9": "超宽 · 电影感" }, - "visualStyle": "视觉风格", - "visualStyleHint": "根据受众选择画面风格,例如:真人风格适合写实剧情,动漫风格适合二次元内容", + "visualStyle": "画面风格", + "visualStyleHint": "选择画面风格,不同风格适合不同类型的作品", "currentConfigSummary": "当前配置:{ratio} · {style},后续生成都会使用此组合", "assetLibraryRatioNote": "资产库比例不受影响", "moreConfig": "更多配置请点击右上角「 配置」按钮", @@ -134,7 +134,16 @@ }, "creating": "AI 创作中...", "ready": "✓ 配置完成,可以进入下一步", - "pleaseInput": "请先输入剧本内容" + "pleaseInput": "请先输入剧本内容", + "longTextDetection": { + "title": "🚀 建议使用智能分集", + "description": "检测到文本约 {count} 字,长文本直接作为单集处理可能导致生成效果不佳。", + "strongRecommend": "强烈建议使用智能分集,AI 将自动识别章节结构,拆分为多集并行处理,显著提升生成质量和效率。", + "continueAnyway": "仍然单集创作", + "smartSplit": "智能分集", + "smartSplitRecommend": "推荐", + "singleEpisodeWarning": "单集模式下,所有内容将作为一集处理" + } }, "execution": { "selectEpisode": "请先选择剧集", diff --git a/messages/zh/workspaceRedesign.json b/messages/zh/workspaceRedesign.json new file mode 100644 index 0000000..7e9b6f2 --- /dev/null +++ b/messages/zh/workspaceRedesign.json @@ -0,0 +1,77 @@ +{ + "pageTitle": "首页重设计测试", + "switchVariant": "切换排版", + "currentVariant": "当前排版", + "inputPlaceholder": "描述你想要创作的故事...", + "startCreation": "开始创作", + "recentProjects": "最近项目", + "viewAll": "查看全部", + "noRecentProjects": "暂无最近项目", + "latestUpdate": "最近更新", + "style": "风格", + "ratio": "比例", + "quality": "质量", + "model": "模型", + "styles": { + "anime": "日系动漫", + "realistic": "写实风格", + "watercolor": "水彩风格", + "cyberpunk": "赛博朋克", + "ghibli": "吉卜力", + "ink": "国风水墨" + }, + "ratios": { + "r16_9": "16:9 横屏", + "r9_16": "9:16 竖屏", + "r1_1": "1:1 方形", + "r4_3": "4:3 经典" + }, + "qualities": { + "standard": "标准", + "high": "高清", + "ultra": "超清" + }, + "quickActions": { + "title": "快速开始", + "fromNovel": "从小说导入", + "fromScript": "从剧本创建", + "fromScratch": "空白创建", + "fromTemplate": "模板创建" + }, + "mockProject": { + "name1": "校园青春物语", + "desc1": "一段关于高中生活的浪漫故事", + "name2": "星际迷航记", + "desc2": "太空冒险科幻短剧", + "name3": "古风仙侠录", + "desc3": "修仙世界的恩怨情仇", + "name4": "都市奇缘", + "desc4": "现代都市中的奇妙遭遇", + "name5": "末日旅人", + "desc5": "后末日世界的生存之旅" + }, + "variantNames": { + "v1": "网格卡片", + "v2": "横向滚动", + "v3": "紧凑列表", + "v4": "突出首项", + "v5": "极简列表" + }, + "variantDescs": { + "v1": "标准5列网格,系统真实卡片风格", + "v2": "一行横滚大卡片,可滑动查看", + "v3": "单行式列表,信息左右分布", + "v4": "首个项目大卡片+右侧小卡片网格", + "v5": "和输入框同宽的极简圆点行" + }, + "episodes": "章节", + "images": "图片", + "videos": "视频", + "updated": "更新于", + "ago": { + "justNow": "刚刚", + "minutesAgo": "{n}分钟前", + "hoursAgo": "{n}小时前", + "daysAgo": "{n}天前" + } +} diff --git a/src/app/[locale]/auth/signin/page.tsx b/src/app/[locale]/auth/signin/page.tsx index 5dbcb4b..3d0ffe4 100644 --- a/src/app/[locale]/auth/signin/page.tsx +++ b/src/app/[locale]/auth/signin/page.tsx @@ -5,6 +5,7 @@ import { signIn } from "next-auth/react" import { useTranslations } from 'next-intl' import Navbar from "@/components/Navbar" import { Link, useRouter } from '@/i18n/navigation' +import { buildAuthenticatedHomeTarget } from '@/lib/home/default-route' export default function SignIn() { const [username, setUsername] = useState("") @@ -31,7 +32,7 @@ export default function SignIn() { } else if (result?.error) { setError(t('loginFailed')) } else { - router.push({ pathname: '/' }) + router.push(buildAuthenticatedHomeTarget()) router.refresh() } } catch { diff --git a/src/app/[locale]/dev/workspace-redesign/InlineSelector.tsx b/src/app/[locale]/dev/workspace-redesign/InlineSelector.tsx new file mode 100644 index 0000000..bacf0e3 --- /dev/null +++ b/src/app/[locale]/dev/workspace-redesign/InlineSelector.tsx @@ -0,0 +1,72 @@ +'use client' + +import { useState, useRef, useEffect } from 'react' +import { AppIcon } from '@/components/ui/icons' + +/** + * 内嵌下拉选择器 + * 显示为紧凑的标签按钮,点击展开向上弹出选项列表 + */ +export function InlineSelector({ + label, + selectedId, + options, + onSelect, + renderLabel, +}: { + label: string + selectedId: string + options: { id: string; labelKey: string; emoji?: string }[] + onSelect: (id: string) => void + renderLabel: (opt: { id: string; labelKey: string; emoji?: string }) => string +}) { + const [open, setOpen] = useState(false) + const ref = useRef(null) + const selected = options.find((o) => o.id === selectedId) + + // 点击外部关闭 + useEffect(() => { + if (!open) return + const handler = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + setOpen(false) + } + } + document.addEventListener('mousedown', handler) + return () => document.removeEventListener('mousedown', handler) + }, [open]) + + return ( +
+ + {open && ( +
+ {options.map((opt) => ( + + ))} +
+ )} +
+ ) +} diff --git a/src/app/[locale]/dev/workspace-redesign/ProjectLayouts.tsx b/src/app/[locale]/dev/workspace-redesign/ProjectLayouts.tsx new file mode 100644 index 0000000..d6aaf56 --- /dev/null +++ b/src/app/[locale]/dev/workspace-redesign/ProjectLayouts.tsx @@ -0,0 +1,239 @@ +'use client' + +/** + * 最近项目布局组件集 + * 5 种不同的排版方式,使用系统实际的卡片设计风格 + */ +import { AppIcon, IconGradientDefs } from '@/components/ui/icons' +import type { MockProject } from './shared' +import { formatTimeAgo } from './shared' + +/** 通用的项目统计行 — 模仿系统真实卡片中的渐变统计 */ +function ProjectStats({ project, t }: { project: MockProject; t: (key: string) => string }) { + return ( +
+
+ ) +} + +/** + * 排版1: 网格卡片 + * 标准 5 列网格,卡片内容模仿系统真实结构(标题+描述+统计+时间) + */ +export function LayoutGrid({ projects, t }: { projects: MockProject[]; t: (key: string, params?: Record) => string }) { + return ( +
+
+

{t('recentProjects')}

+ +
+
+ {projects.map((p) => ( +
+
+
+

+ {p.name} +

+
+ +

{p.description}

+
+ +
+ + {formatTimeAgo(p.updatedAt, t)} +
+
+
+ ))} +
+
+ ) +} + +/** + * 排版2: 横向滚动 + * 一排横滚大卡片,更有沉浸感 + */ +export function LayoutScroll({ projects, t }: { projects: MockProject[]; t: (key: string, params?: Record) => string }) { + return ( +
+
+

{t('recentProjects')}

+ +
+
+ {projects.map((p) => ( +
+
+
+

+ {p.name} +

+
+ +

{p.description}

+
+ +
+ + {formatTimeAgo(p.updatedAt, t)} +
+
+
+ ))} +
+
+ ) +} + +/** + * 排版3: 紧凑列表 + * 左右布局的一行式列表,信息紧凑 + */ +export function LayoutList({ projects, t }: { projects: MockProject[]; t: (key: string, params?: Record) => string }) { + return ( +
+
+

{t('recentProjects')}

+ +
+
+ {projects.map((p) => ( +
+
+

+ {p.name} +

+

{p.description}

+
+
+ +
+
+ + {formatTimeAgo(p.updatedAt, t)} +
+ +
+ ))} +
+
+ ) +} + +/** + * 排版4: 突出首项 + * 第一个项目大卡片占满左侧,右侧两列堆叠小卡片 + */ +export function LayoutFeatured({ projects, t }: { projects: MockProject[]; t: (key: string, params?: Record) => string }) { + const [first, ...rest] = projects + + return ( +
+
+

{t('recentProjects')}

+ +
+
+ {/* 大卡片 */} + {first && ( +
+
+
+
+
{t('latestUpdate')}
+

+ {first.name} +

+
+ +

{first.description}

+
+
+
+ +
+ + {formatTimeAgo(first.updatedAt, t)} +
+
+
+
+ )} + {/* 小卡片网格 */} +
+ {rest.map((p) => ( +
+
+
+

+ {p.name} +

+

{p.description}

+
+ + {formatTimeAgo(p.updatedAt, t)} +
+
+
+ ))} +
+
+
+ ) +} + +/** + * 排版5: 极简圆点列表 + * 和输入框同宽的极简列表,仅显示项目名和关键数据 + */ +export function LayoutMinimalList({ projects, t }: { projects: MockProject[]; t: (key: string, params?: Record) => string }) { + return ( +
+
+

{t('recentProjects')}

+ +
+
+ {projects.map((p) => ( +
+
+ + {p.name} +
+
+ {p.stats.episodes} {t('episodes')} + {p.stats.images} {t('images')} + {formatTimeAgo(p.updatedAt, t)} + +
+
+ ))} +
+
+ ) +} diff --git a/src/app/[locale]/dev/workspace-redesign/VariantClearBreath.tsx b/src/app/[locale]/dev/workspace-redesign/VariantClearBreath.tsx new file mode 100644 index 0000000..2df455f --- /dev/null +++ b/src/app/[locale]/dev/workspace-redesign/VariantClearBreath.tsx @@ -0,0 +1,147 @@ +'use client' + +/** + * 清澈呼吸 — 输入区域 + * Apple 风格呼吸光晕 + 下拉标签选项 + * 底部排版由 page.tsx 注入 + */ +import { useState } from 'react' +import { useTranslations } from 'next-intl' +import { AppIcon } from '@/components/ui/icons' +import { InlineSelector } from './InlineSelector' +import { + STYLE_OPTIONS, + RATIO_OPTIONS, + QUALITY_OPTIONS, +} from './shared' + +export default function VariantClearBreath({ children }: { children?: React.ReactNode }) { + const t = useTranslations('workspaceRedesign') + const [selectedStyle, setSelectedStyle] = useState('anime') + const [selectedRatio, setSelectedRatio] = useState('16:9') + const [selectedQuality, setSelectedQuality] = useState('high') + const [inputValue, setInputValue] = useState('') + + return ( +
+ {/* 自定义呼吸动画 */} + + +
+
+

+ ✨ {t('quickActions.title')} +

+

{t('inputPlaceholder')}

+
+ + {/* 呼吸光晕容器 */} +
+
+
+
+ +
+