diff --git a/web/public/short-drama-styles/black-white-noir.jpg b/web/public/short-drama-styles/black-white-noir.jpg new file mode 100644 index 000000000..176efca6d Binary files /dev/null and b/web/public/short-drama-styles/black-white-noir.jpg differ diff --git a/web/public/short-drama-styles/clay-stop-motion.jpg b/web/public/short-drama-styles/clay-stop-motion.jpg new file mode 100644 index 000000000..5b57944fd Binary files /dev/null and b/web/public/short-drama-styles/clay-stop-motion.jpg differ diff --git a/web/public/short-drama-styles/comic-pop.jpg b/web/public/short-drama-styles/comic-pop.jpg new file mode 100644 index 000000000..b05a503ce Binary files /dev/null and b/web/public/short-drama-styles/comic-pop.jpg differ diff --git a/web/public/short-drama-styles/cyberpunk-neon.jpg b/web/public/short-drama-styles/cyberpunk-neon.jpg new file mode 100644 index 000000000..1093a7777 Binary files /dev/null and b/web/public/short-drama-styles/cyberpunk-neon.jpg differ diff --git a/web/public/short-drama-styles/future-tech.jpg b/web/public/short-drama-styles/future-tech.jpg new file mode 100644 index 000000000..5290aca85 Binary files /dev/null and b/web/public/short-drama-styles/future-tech.jpg differ diff --git a/web/public/short-drama-styles/nature-healing.jpg b/web/public/short-drama-styles/nature-healing.jpg new file mode 100644 index 000000000..432aed5f7 Binary files /dev/null and b/web/public/short-drama-styles/nature-healing.jpg differ diff --git a/web/public/short-drama-styles/retro-hong-kong.jpg b/web/public/short-drama-styles/retro-hong-kong.jpg new file mode 100644 index 000000000..6392d83aa Binary files /dev/null and b/web/public/short-drama-styles/retro-hong-kong.jpg differ diff --git a/web/public/short-drama-styles/space-opera.jpg b/web/public/short-drama-styles/space-opera.jpg new file mode 100644 index 000000000..b71190f3f Binary files /dev/null and b/web/public/short-drama-styles/space-opera.jpg differ diff --git a/web/public/short-drama-styles/storybook-fantasy.jpg b/web/public/short-drama-styles/storybook-fantasy.jpg new file mode 100644 index 000000000..5a1cf6afb Binary files /dev/null and b/web/public/short-drama-styles/storybook-fantasy.jpg differ diff --git a/web/public/short-drama-styles/surreal-dream.jpg b/web/public/short-drama-styles/surreal-dream.jpg new file mode 100644 index 000000000..5a3ad68a9 Binary files /dev/null and b/web/public/short-drama-styles/surreal-dream.jpg differ diff --git a/web/src/components/canvas/canvas-style-picker-modal.tsx b/web/src/components/canvas/canvas-style-picker-modal.tsx index 2dac53080..bbc33f388 100644 --- a/web/src/components/canvas/canvas-style-picker-modal.tsx +++ b/web/src/components/canvas/canvas-style-picker-modal.tsx @@ -1,6 +1,6 @@ -import type { CSSProperties } from "react"; -import { Check, Clapperboard, Palette, Sparkles } from "lucide-react"; -import { Modal } from "antd"; +import { useState, type CSSProperties } from "react"; +import { Check, Clapperboard, Eye, Palette, Sparkles } from "lucide-react"; +import { Button, Modal } from "antd"; import { canvasThemes } from "@/lib/canvas-theme"; import { useThemeStore } from "@/stores/use-theme-store"; @@ -152,6 +152,196 @@ export const canvasStylePresets: CanvasStylePreset[] = [ ].join("\n"), imageUrl: "/short-drama-styles/fantasy-3d.jpg", }, + { + id: "future-tech", + title: "未来科技", + category: "科幻实拍", + description: "洁净白灰空间、精密工业结构与克制冷光构成可信近未来;科技来自功能与材质,不依赖满屏霓虹和悬浮界面。", + tags: ["科技", "近未来", "硬科幻"], + prompt: [ + "【项目定位】近未来中国科技题材的写实硬科幻短剧体系,以可推演的技术、清晰功能结构和克制先进感为核心;世界比当下领先约十至三十年,所有设备、服装和空间都必须能解释用途,不使用空泛的科幻装饰。", + "【项目色彩系统】全项目色板使用权重约为 65% 冷白、浅灰、钛金属和透明材质,25% 石墨黑、深海蓝和低饱和工业色,10% 青绿或电蓝状态指示色。高亮色只用于设备状态、权限和风险提示,不扩散为环境染色。", + "【角色设计系统】角色保持真实东亚骨相、自然年龄与职业可信度,通过精确剪裁、功能装备和身份识别细节区分科研、工程、医疗、安保与管理角色;义体或可穿戴设备必须符合人体结构并具有明确功能。", + "【服饰与材质系统】建立模块化工作服、防护服、通勤装和轻型外骨骼体系,统一使用哑光技术织物、钛合金、陶瓷、透明聚合物和精密玻璃;接缝、扣具、传感器与磨损位置必须符合使用逻辑。", + "【建筑世界观】统一使用可信的中国近未来实验室、数据中心、交通枢纽、智能住宅、先进制造与城市公共设施;空间强调结构、维护通道、导视和设备尺度,科技升级建立在现有城市之上而非凭空替换。", + "【影像与动态基线】全片保持清晰冷静、低噪点和高材质可读性,光源来自顶灯、工作灯、屏幕与自然天光;动态强调精密操作、机械反馈和空间秩序,具体机位与节奏由分镜决定。", + "【资产一致性】建立设备型号、接口协议、状态灯职责、角色权限色、服装模块和空间导视手册;同类设备共享结构语言,升级与损坏必须形成可追踪版本。", + "【全局禁用】禁止无功能全息屏、满屏蓝色 HUD、随机发光线、霓虹城市套壳、塑料太空服、错误机械结构、设备尺寸漂移、英文乱码和把未来科技等同于赛博朋克。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/future-tech.jpg", + }, + { + id: "cyberpunk-neon", + title: "赛博霓虹", + category: "科幻实拍", + description: "高密度亚洲夜城、潮湿旧材质与受控霓虹形成技术失衡的近未来;人物身份由义体、阶层服装和企业色区分。", + tags: ["赛博朋克", "霓虹", "都市夜景"], + prompt: [ + "【项目定位】亚洲高密度都市中的赛博朋克短剧体系,视觉核心是先进技术与老旧城市并存、企业秩序与街头生活碰撞;霓虹只作为商业、交通与身份系统的一部分,世界必须保留真实生活痕迹。", + "【项目色彩系统】全项目色板使用权重约为 55% 沥青黑、旧水泥、潮湿金属和烟灰,30% 冷青、脏黄与屏幕白,15% 洋红、警戒红或企业识别色。每种高亮色绑定阵营或功能,避免无差别彩虹霓虹。", + "【角色设计系统】角色采用真实东亚骨相和明确阶层差异,义眼、接口、义肢与皮下设备遵循统一技术代际;街头角色、企业雇员和执法人员通过剪影、改装程度、磨损与身份标记区分。", + "【服饰与材质系统】建立机能夹克、防水层、企业制服、旧式工装和改装配件库,统一使用磨损尼龙、橡胶、旧皮革、氧化金属、透明塑料和低亮度电子元件;服装必须可活动、可收纳并符合天气。", + "【建筑世界观】统一使用高密度亚洲街区、旧楼加建、地下交通、企业塔楼、维修铺、夜市与狭窄居住单元,中文标识与在地设施可信;新技术以加装、覆盖和侵入旧城市的方式出现。", + "【影像与动态基线】全片保持夜景可读、潮湿反射受控、实景光源有出处,动态兼具街头拥挤感与企业空间秩序;雨、烟和故障只在剧情需要时出现,不能遮盖人物表演与关键信息。", + "【资产一致性】建立企业标识、街区导视、义体代际、设备接口、角色改装和服装磨损版本;同一地点的招牌、线路与空间关系跨集保持稳定。", + "【全局禁用】禁止纯装饰霓虹、所有角色都装义体、无来源彩色轮廓光、日文城市替代中国语境、英文乱码、全屏雨雾、过曝招牌、随机机械纹身和未来设备无功能逻辑。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/cyberpunk-neon.jpg", + }, + { + id: "retro-hong-kong", + title: "复古港风", + category: "年代实拍", + description: "八九十年代华语都市的胶片色、密集街区与生活化服饰;用钨丝暖光、青绿阴影和真实时代器物建立怀旧感。", + tags: ["港风", "胶片", "年代都市"], + prompt: [ + "【项目定位】八十至九十年代华语都市题材的复古港风短剧体系,以真实时代生活、紧凑街区和胶片摄影质感为核心;怀旧来自服化道、空间和人物状态,不靠泛黄滤镜或明星仿妆。", + "【项目色彩系统】全项目色板使用权重约为 50% 奶油白、旧墙灰、墨绿和木褐,35% 钨丝暖黄、青绿色阴影、牛仔蓝与暗红,15% 招牌红、出租车色或角色识别色。色彩保持胶片宽容度和自然肤色。", + "【角色设计系统】统一采用真实东亚骨相与明确时代发型、妆容和体态,按职业与阶层建立差异;角色通过眉形、卷发、短发、眼镜、手表与稳定服装剪影识别,不照搬具体明星面孔。", + "【服饰与材质系统】建立宽肩西装、衬衫、针织、牛仔、风衣、连衣裙和工装衣橱,使用真实棉、羊毛、皮革、涤纶和金属配件;版型、花纹、纽扣、鞋履与随身物件锁定明确年代。", + "【建筑世界观】统一使用骑楼、旧式住宅、茶餐厅、录像厅、办公室、街市、码头、巴士与密集招牌街道,保留窗机、瓷砖、铁闸、电话和时代交通工具;不得混入智能手机与当代城市家具。", + "【影像与动态基线】全片使用适度胶片颗粒、柔和高光、钨丝灯与窗外冷光的自然混合,动态保持手持观察和经典剧情片的稳健节奏;具体景别、变焦与运动由分镜决定。", + "【资产一致性】建立年代器物清单、角色衣橱、招牌字形、交通工具、室内材质和胶片色彩基线;时间推进通过服装、发型与城市更新版本体现。", + "【全局禁用】禁止一键泛黄滤镜、现代手机与车辆、错误年代品牌、过量霓虹、日系昭和混用、具体明星换脸、影楼旗袍写真、干净到失真的街景和随机繁体乱码。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/retro-hong-kong.jpg", + }, + { + id: "clay-stop-motion", + title: "黏土定格", + category: "定格动画", + description: "保留手塑痕迹的黏土角色、微缩布景与逐帧动作;统一模型比例、材料颗粒和灯光尺度,形成温暖可触的手工世界。", + tags: ["黏土", "定格", "手工质感"], + prompt: [ + "【项目定位】以手工黏土角色和微缩布景制作的定格动画短剧体系,视觉核心是可触摸的材料、真实模型尺度与逐帧表演;全项目保留适度手塑痕迹,不伪装成光滑三维动画。", + "【项目色彩系统】全项目色板使用权重约为 55% 陶土、米白、旧木和纸张原色,30% 角色主色与场景功能色,15% 珊瑚红、芥末黄、湖蓝或项目点睛色。颜色带有实体颜料的轻微不均匀感。", + "【角色设计系统】角色采用统一头身、眼球、嘴型替换与骨架关节规则,通过鼻形、发型、体型和服装剪影区分;指纹、接缝与替换口型可适度保留,但正侧背结构必须一致且可制作。", + "【服饰与材质系统】衣物使用薄黏土、布料、毛线、纸张与细金属件组合,木、石、玻璃和食物以微缩材料模拟;每种材质保持可辨的颗粒、厚度与手工边缘,避免统一塑料表面。", + "【建筑世界观】统一使用可搭建的微缩住宅、街道、店铺、自然景观与室内布景,结构受真实模型尺寸、灯具和摄影空间约束;背景与道具遵循同一缩尺和手工制作语言。", + "【影像与动态基线】全片保持棚内微缩摄影的真实景深、柔和体积光与轻微逐帧节奏,动作强调清晰关键姿势、重量和停顿;不得用运动模糊掩盖定格特征,具体表演由分镜决定。", + "【资产一致性】建立角色骨架、替换脸、手型、标准色泥、缩尺、布景模块与道具制作表;损坏、换装和表情组件必须按资产版本维护。", + "【全局禁用】禁止光滑 CGI、塑料玩具反光、真实人类皮肤、模型比例漂移、关节凭空变形、材质尺度错误、过度景深虚化、随机指纹污渍和布景看似无限真实空间。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/clay-stop-motion.jpg", + }, + { + id: "black-white-noir", + title: "黑白默片", + category: "风格化实拍", + description: "以黑白灰阶、硬朗光影和克制表演建立无对白也可读的戏剧性;时代质感来自构图、材质与表演,不靠复古滤镜。", + tags: ["黑白", "默片", "电影感"], + prompt: [ + "【项目定位】以黑白电影和默片叙事为核心的风格化实拍短剧体系,强调轮廓、构图、肢体表演和视觉隐喻;可有少量时代道具,但不把项目固定为某一历史年代。", + "【项目色彩系统】全项目使用黑、白和五级中性灰作为主色,允许极少量单色强调作为叙事线索;明暗关系优先服务人物层次与信息可读性,禁止把画面压成一团黑或过度漂白。", + "【角色设计系统】角色保持真实东亚骨相与明确剪影,妆发和年龄感自然稳定;人物通过帽檐、领型、姿态、手势和光影落点区分,正侧背设定必须在灰阶中仍然清楚。", + "【服饰与材质系统】服装按角色身份建立黑白灰明度差、纹理密度和轮廓结构,使用羊毛、棉、皮革、金属与玻璃的真实反差;图案不依赖颜色,必须通过材质和明度可辨。", + "【建筑世界观】统一使用几何清晰、层次明确的城市街道、室内、车站、剧院和工业空间;墙面、玻璃、砖石、金属和雾气都以灰阶材质库控制,不添加无叙事的奇观背景。", + "【影像与动态基线】使用硬光、侧光、背光和局部高光塑造戏剧性,允许少量胶片颗粒与光晕;表演通过停顿、视线、走位和夸张但可信的肢体传达,具体节奏由分镜决定。", + "【资产一致性】建立灰阶色卡、角色轮廓、帽饰与道具明度表、光影基线和场景构图规则;同一角色的服装纹理和标志道具在不同曝光下仍需稳定。", + "【全局禁用】禁止随意套用棕色复古滤镜、画面全黑、现代彩色屏幕抢戏、过度烟雾、夸张舞台妆、无意义鱼眼变形、角色剪影混淆和字幕水印。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/black-white-noir.jpg", + }, + { + id: "space-opera", + title: "太空歌剧", + category: "科幻动画", + description: "宏大星际尺度与鲜明阵营服饰结合可信航天结构;用色彩、徽记和材质建立政治与冒险叙事,而不是堆砌宇宙奇观。", + tags: ["星际", "阵营", "史诗"], + prompt: [ + "【项目定位】东方叙事语境下的太空歌剧项目,融合星际航行、阵营政治、家族关系与冒险成长;世界观允许宏大奇观,但人物动机、技术限制和空间尺度必须清楚可追踪。", + "【项目色彩系统】全项目色板使用权重约为 45% 深空黑、冷灰、银白和舱体白,35% 阵营主色与舰队识别色,20% 星云紫、能源蓝或警戒橙作为叙事强调;每个阵营只拥有稳定主色和辅助色。", + "【角色设计系统】角色采用真实东方骨相或统一动画化比例,通过军衔、阵营徽记、发型、体型和功能装备区分;太空服、驾驶服和礼服必须适配人体活动,角色脸部与身体比例跨视角稳定。", + "【服饰与材质系统】建立舱内工作服、战术航天服、舰队制服、外交礼服和维修装模块,统一使用复合纤维、陶瓷、金属、透明面罩和软质密封件;接口、扣件、徽记与生命保障装置具有功能逻辑。", + "【建筑世界观】统一使用环形空间站、舰桥、推进舱、殖民地、轨道电梯和低重力生活区,结构遵循压力舱、维护通道、重力与照明逻辑;奇观服务于文明尺度,不随机拼贴异星建筑。", + "【影像与动态基线】保持深空黑与舱内工作光的层次,使用克制体积光和可信屏幕反射;动作体现惯性、失重、穿戴装备和空间限制,具体战斗与运镜由分镜决定。", + "【资产一致性】建立舰船型号、阵营徽记、舱体材质、接口标准、武器尺寸、角色制服和能源颜色版本表;任何舰船升级、损坏与换装都要有剧情原因。", + "【全局禁用】禁止现实宇航服直接套用、星球大小失真、所有阵营同一颜色、无功能 HUD、魔法与科技规则混杂、舰船结构穿透、角色装备随机变化和版权品牌标识。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/space-opera.jpg", + }, + { + id: "comic-pop", + title: "漫画分镜", + category: "风格化动画", + description: "高对比块面、网点纹理与漫画格律构成可读的动作喜剧视觉;夸张来自构图、拟声和姿态,角色资产仍保持连续性。", + tags: ["漫画", "动作", "高对比"], + prompt: [ + "【项目定位】面向动作、喜剧与都市冒险的漫画分镜风格,统一使用清晰外轮廓、平涂块面、网点纹理和有节奏的画格感;画面可以夸张,但角色关系、动作方向与空间连续性必须可靠。", + "【项目色彩系统】全项目以米白、墨黑和中性灰为结构底色,30% 使用高饱和红、黄、蓝或项目主色,10% 使用单一冲击色承担危险、速度或情绪重点;颜色必须形成角色和阵营识别。", + "【角色设计系统】统一采用稳定头身比例、强轮廓、清晰发型和可重复的表情符号;角色通过剪影、服装块面、姿态和道具区分,夸张表情不得改变基本脸型与身份。", + "【服饰与材质系统】服饰细节简化成大色块、粗线纹样和少量高光,棉、皮革、金属、牛仔和玻璃通过线条密度与高光形状区分;战斗或动作服装必须兼顾可动性。", + "【建筑世界观】使用可快速识别的街区、商店、学校、办公室、车站和屋顶等都市空间,背景以几何块面、透视线和有限网点表达;场景不抢角色轮廓,也不使用现实品牌标识。", + "【影像与动态基线】动作使用速度线、冲击形状、停格姿势和清晰重心,切换节奏由分镜控制;镜头可以模拟画格切分,但不得牺牲表情、手势和动作方向的可读性。", + "【资产一致性】建立角色标准角度、表情表、线宽、网点密度、拟声字形和动作特效模板;不同镜头共享同一线条、色块与纹理规则。", + "【全局禁用】禁止写实照片与漫画角色混用、线条粗细随机、所有画面满屏速度线、复杂背景压住人物、拟声文字乱码、角色换脸、四肢结构错误和过度血腥猎奇。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/comic-pop.jpg", + }, + { + id: "storybook-fantasy", + title: "绘本童话", + category: "插画动画", + description: "纸张颗粒、柔和色块和手绘边缘构成温暖童话世界;角色表情与轮廓清楚,避免把绘本感做成泛化的柔焦滤镜。", + tags: ["绘本", "童话", "温暖"], + prompt: [ + "【项目定位】面向成长、亲子与寓言叙事的绘本童话动画体系,统一使用手绘边缘、纸张颗粒、有限色块和可读角色表情;故事世界可以奇幻,但情绪和人物关系保持温暖、具体且可理解。", + "【项目色彩系统】全项目色板使用权重约为 55% 纸张暖白、雾蓝、鼠尾草绿和浅木色,30% 角色与场景主色,15% 蜜糖黄、珊瑚红或星光金作为叙事点色;避免所有画面都成为粉彩渐变。", + "【角色设计系统】角色采用统一绘本头身、圆润但有差异的轮廓和清晰眼鼻嘴比例,通过帽子、发型、耳朵、体型与服装区分;正侧背视图必须保留同一手绘造型特征。", + "【服饰与材质系统】衣服使用布料、针织、皮革、木扣、纸张和毛线等可触摸材质,纹样简化为少量重复元素;角色标志物件和衣领、袖口、鞋履保持稳定。", + "【建筑世界观】统一使用村庄、森林、阁楼、车站、河岸、市场与小型城堡等可读场景,建筑结构偏手工搭建和绘本透视;奇幻元素从生活物件变形而来,不做无逻辑异域拼贴。", + "【影像与动态基线】保持柔和但有方向的光影、纸张纹理和有限动画节奏,动作强调呼吸、重心、眼神与衣摆跟随;具体动作与镜头调度交由分镜决定。", + "【资产一致性】建立标准色卡、纸张纹理、笔刷边缘、角色比例、表情表、场景模块和魔法点色规则;新资产必须匹配已有线条和颗粒尺度。", + "【全局禁用】禁止塑料 3D、泛滥柔焦、角色同脸、过度糖果色、阴影方向漂移、复杂文字水印、细节密度失控、随机改发型和把儿童角色做成恐怖谷。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/storybook-fantasy.jpg", + }, + { + id: "surreal-dream", + title: "超现实梦境", + category: "风格化实拍", + description: "现实材质与不可能空间并置,以镜像、尺度错位和雾化光线表达梦境;所有超现实变化服务角色心理和叙事线索。", + tags: ["梦境", "超现实", "心理"], + prompt: [ + "【项目定位】以现实人物进入不可能空间为核心的超现实梦境短剧体系,梦境不是随机奇观,而是由人物记忆、欲望、恐惧和选择构成的可追踪视觉系统;现实段落与梦境段落必须有明确差异和回返规则。", + "【项目色彩系统】现实段落使用自然中性灰与低饱和环境色,梦境段落使用 35% 雾白、浅金、冷青和暗紫,15% 固定象征色承载角色心理线索;同一象征色不能在不同角色间随意换义。", + "【角色设计系统】角色保持真实东亚骨相、年龄与身体比例,梦境只允许在发型、服装、尺度、影子或局部材质上做有规则的变形;正侧背结构必须仍能确认是同一角色。", + "【服饰与材质系统】现实服装遵循生活逻辑,梦境服装可以通过重复、倒置、透明、过大或过小表达心理,但面料、纽扣、鞋履和标志物件需要保持可追踪变体。", + "【建筑世界观】统一使用镜像房间、无尽楼梯、漂浮街道、倒置建筑、空旷大厅和雾中自然等空间语法;每种不可能空间绑定一个叙事规则,不把随机生成的城堡和星空堆在一起。", + "【影像与动态基线】现实使用可信光源和稳定镜头,梦境使用慢速漂浮、镜面反射、尺度变化与非连续转场;特效保持节制,具体梦境动作和节拍由分镜决定。", + "【资产一致性】建立象征色、梦境规则、角色标志物件、空间变形前后对照、镜面材质和转场语法表;每次变形都能对应剧情节点并可恢复。", + "【全局禁用】禁止无因由的随机变形、全屏烟雾、过度镜面、所有空间都漂浮、梦境与现实没有区别、角色五官融化、符号含义漂移和用噪点掩盖生成错误。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/surreal-dream.jpg", + }, + { + id: "nature-healing", + title: "自然疗愈", + category: "真人实拍", + description: "真实自然光、植物与手作生活构成松弛的在地质感;以季节、声音和微小行动表达疗愈,不做广告式空镜。", + tags: ["疗愈", "自然", "慢生活"], + prompt: [ + "【项目定位】面向家庭关系、返乡、成长与情绪修复的自然疗愈真人短剧体系,核心是人物在具体自然环境中的生活行动和关系变化;自然不是装饰背景,而是参与叙事的真实空间。", + "【项目色彩系统】全项目色板使用权重约为 55% 叶绿、苔藓绿、土色和米白,30% 木材、石头、天空与季节环境色,15% 赭红、芥末黄或人物识别色;保持自然白平衡和季节连续性。", + "【角色设计系统】角色采用真实东亚面孔、自然年龄与生活状态,保留晒痕、发丝、体态和劳动痕迹;通过衣着层次、动作习惯、发型与随身物件建立差异,不使用商业广告式精致人物。", + "【服饰与材质系统】建立棉麻、针织、旧牛仔、雨具、工作服、帆布鞋与草帽衣橱,材质保留褶皱、泥土、汗水和使用痕迹;固定角色的外套、包和手作物件形成识别资产。", + "【建筑世界观】统一使用山村住宅、林间小屋、农场、茶室、河岸、菜园、集市和小城边缘空间,保留木、石、土、旧墙、工具与本地生活设施;建筑应有地域和季节证据。", + "【影像与动态基线】采用真实日光、阴天漫射、篝火与室内暖灯,允许风、雨、昆虫和环境声进入画面;动态观察人物的微小行动与关系停顿,避免只拍漂亮自然空镜。", + "【资产一致性】建立季节色卡、植物与农具库、住宅布局、角色衣橱、手作物件和天气连续性记录;季节变化应带来可见但渐进的资产版本。", + "【全局禁用】禁止商业民宿广告感、过度航拍、饱和滤镜、欧美乡村替代中国在地生活、干净无人的自然空间、人物永远精致、随机换季和把疗愈做成空洞慢镜头。", + PROJECT_STYLE_SCOPE, + ].join("\n"), + imageUrl: "/short-drama-styles/nature-healing.jpg", + }, { id: "real-life-documentary", title: "现实生活纪实", @@ -175,37 +365,77 @@ export const canvasStylePresets: CanvasStylePreset[] = [ export function CanvasStylePickerModal({ open, value, onClose, onSelect }: { open: boolean; value?: string; onClose: () => void; onSelect: (preset: CanvasStylePreset) => void }) { const theme = canvasThemes[useThemeStore((state) => state.theme)]; + const [detailPreset, setDetailPreset] = useState(null); return ( - -
-
- -
-
选择项目画风短剧视觉预设
-
选中的画风会作为普通风格板节点加入当前画布,后续角色、分镜和视频提示词会复用。
+ <> + +
+
+ +
+
选择项目画风{canvasStylePresets.length} 套视觉预设
+
画风会统一角色三视图、项目资产、分镜和视频提示词。
+
+ 先看规范,再做选择 +
+
+ {canvasStylePresets.map((preset) => { + const active = preset.id === value; + return ( +
+ + +
+ ); + })}
- 先选媒介,再做细节 -
-
- {canvasStylePresets.map((preset) => { - const active = preset.id === value; - return ( - - ); - })} +
图片是风格示意,实际效果取决于模型、角色参考图与分镜提示词。
-
图片是风格示意,实际效果取决于模型、角色参考图与分镜提示词。
-
+
+ setDetailPreset(null)} onSelect={(preset) => { setDetailPreset(null); onSelect(preset); }} /> + + ); +} + +export function CanvasStyleDetailModal({ open, preset, selected = false, onClose, onSelect }: { open: boolean; preset: CanvasStylePreset | null; selected?: boolean; onClose: () => void; onSelect?: (preset: CanvasStylePreset) => void }) { + const theme = canvasThemes[useThemeStore((state) => state.theme)]; + const sections = preset ? parseStyleSections(preset.prompt) : []; + return ( + + {preset ?
+ +
+
+
{preset.category}
+

{preset.title}

+

{preset.description}

+
{preset.tags.map((tag) => {tag})}
+
+
+ {sections.map((section) =>

{section.title}

{section.content}

)} +
+
角色三视图会自动提取其中的色彩、造型和材质规范
{onSelect ? : null}
+
+
: null}
); } + +function parseStyleSections(prompt: string) { + return prompt.split("\n").map((line) => { + const match = line.match(/^【([^】]+)】(.*)$/); + return match ? { title: match[1], content: match[2] } : { title: "补充规范", content: line }; + }).filter((section) => section.content); +} diff --git a/web/src/pages/projects/detail/assets.tsx b/web/src/pages/projects/detail/assets.tsx index ec338cbfb..5e95eba00 100644 --- a/web/src/pages/projects/detail/assets.tsx +++ b/web/src/pages/projects/detail/assets.tsx @@ -5,6 +5,7 @@ import { Box, Check, ChevronDown, FileText, Image as ImageIcon, Link2, Music2, P import { WorkspaceState } from "@/components/layout/workspace-state"; import { WorkspaceSignalIcon } from "@/components/ui/aceternity/workspace-signal-icon"; +import { canvasStylePresets } from "@/components/canvas/canvas-style-picker-modal"; import { resourceFileUrl, resourceIdFromStorageKey } from "@/services/api/resources"; import { bindProjectCharacterVoice, @@ -99,7 +100,8 @@ export default function ProjectAssetsView({ detail, refreshProject }: ProjectDet const model = effectiveConfig.imageModel || effectiveConfig.model; const config = { ...effectiveConfig, model }; if (!isAiConfigReady(config, model)) throw new Error("请先在设置中配置可用的图片模型"); - await generateCharacterTurnaround({ projectId: detail.project.id, assetId: asset.id, versionId: asset.character.versionId, name: asset.title, definition: asset.character.definition, config }); + const projectStyle = canvasStylePresets.find((preset) => preset.id === detail.project.stylePresetId); + await generateCharacterTurnaround({ projectId: detail.project.id, assetId: asset.id, versionId: asset.character.versionId, name: asset.title, definition: asset.character.definition, projectStyle, config }); return getProjectCharacter(detail.project.id, asset.id); }, onSuccess: (result) => { syncPersonalCharacterProjection(result.asset); done("三视图已生成并绑定到新角色版本"); }, diff --git a/web/src/pages/projects/detail/project-character-media.ts b/web/src/pages/projects/detail/project-character-media.ts index 0de595cb7..0f4f3daf8 100644 --- a/web/src/pages/projects/detail/project-character-media.ts +++ b/web/src/pages/projects/detail/project-character-media.ts @@ -1,19 +1,21 @@ import { runBackendCanvasGenerationTask } from "@/lib/canvas/canvas-project-generation"; import type { AiConfig } from "@/stores/use-config-store"; -export async function generateCharacterTurnaround(input: { projectId: string; assetId: string; versionId: string; name: string; definition: Record; config: AiConfig }) { - const prompt = characterTurnaroundPrompt(input.name, input.definition); +type ProjectStylePrompt = { id: string; title: string; prompt: string }; + +export async function generateCharacterTurnaround(input: { projectId: string; assetId: string; versionId: string; name: string; definition: Record; projectStyle?: ProjectStylePrompt; config: AiConfig }) { + const prompt = characterTurnaroundPrompt(input.name, input.definition, input.projectStyle); await runBackendCanvasGenerationTask({ projectId: input.projectId, nodeId: `character-turnaround:${input.assetId}`, mode: "image", prompt, config: { ...input.config, model: input.config.imageModel || input.config.model, count: "1" }, - metadata: { operation: "character_turnaround", characterAssetId: input.assetId, resolvedCharacterVersions: [{ assetId: input.assetId, versionId: input.versionId }] }, + metadata: { operation: "character_turnaround", characterAssetId: input.assetId, stylePresetId: input.projectStyle?.id, resolvedCharacterVersions: [{ assetId: input.assetId, versionId: input.versionId }] }, }); } -export function characterTurnaroundPrompt(name: string, definition: Record) { +export function characterTurnaroundPrompt(name: string, definition: Record, projectStyle?: ProjectStylePrompt) { const visual = [definition.role, definition.appearance, definition.physique, definition.clothing, definition.personality, definition.props, definition.consistencyPrompt, definition.multiViewPrompt] .map((value) => String(value || "").trim()) .filter(Boolean) @@ -23,7 +25,23 @@ export function characterTurnaroundPrompt(name: string, definition: Record { + const title = line.match(/^【([^】]+)】/)?.[1]; + return title ? characterSections.has(title) : false; + }); + return [ + `项目画风:${projectStyle.title}。角色造型、配色、服装材质与最终渲染媒介必须遵循以下规范:`, + ...rules, ].join("\n"); } diff --git a/web/src/pages/projects/detail/settings.tsx b/web/src/pages/projects/detail/settings.tsx index c4c375a7b..18d5770aa 100644 --- a/web/src/pages/projects/detail/settings.tsx +++ b/web/src/pages/projects/detail/settings.tsx @@ -1,9 +1,9 @@ import { useEffect, useMemo, useState, type ReactNode } from "react"; import { useMutation } from "@tanstack/react-query"; -import { App, Button, Input, Modal, Select, Tooltip } from "antd"; -import { Archive, Check, Save, ShieldAlert } from "lucide-react"; +import { App, Button, Input, Modal, Select } from "antd"; +import { Archive, Check, Eye, Save, ShieldAlert } from "lucide-react"; -import { canvasStylePresets } from "@/components/canvas/canvas-style-picker-modal"; +import { CanvasStyleDetailModal, canvasStylePresets, type CanvasStylePreset } from "@/components/canvas/canvas-style-picker-modal"; import { updateProject } from "@/services/api/projects"; import type { ProjectDetailViewProps } from "./shared"; @@ -16,6 +16,7 @@ export default function ProjectSettingsView({ detail, refreshProject }: ProjectD const [aspectRatio, setAspectRatio] = useState(project.aspectRatio); const [sourceType, setSourceType] = useState(project.sourceType); const [stylePresetId, setStylePresetId] = useState(project.stylePresetId || ""); + const [styleDetail, setStyleDetail] = useState(null); const [archiveOpen, setArchiveOpen] = useState(false); useEffect(() => { setName(project.name); setDescription(project.description || ""); setAspectRatio(project.aspectRatio); setSourceType(project.sourceType); setStylePresetId(project.stylePresetId || ""); }, [project]); const dirty = useMemo(() => name.trim() !== project.name || description !== (project.description || "") || aspectRatio !== project.aspectRatio || sourceType !== project.sourceType || stylePresetId !== (project.stylePresetId || ""), [aspectRatio, description, name, project, sourceType, stylePresetId]); @@ -41,7 +42,7 @@ export default function ProjectSettingsView({ detail, refreshProject }: ProjectD
{canvasStylePresets.map((preset) => { const active = preset.id === stylePresetId; - return ; + return
; })}
@@ -54,6 +55,7 @@ export default function ProjectSettingsView({ detail, refreshProject }: ProjectD setArchiveOpen(false)} onOk={() => archiveMutation.mutate()} width={440} styles={{ body: { paddingTop: 12 } }}>

{project.status === "archived" ? "恢复后项目会重新进入可编辑状态。" : "归档不会删除章节、画布或资产,画布文档仍可在创作画布中打开。"}

+ setStyleDetail(null)} onSelect={(preset) => { setStylePresetId(preset.id); setStyleDetail(null); }} /> ); }