diff --git a/lib/prompts/novel-promotion/select_prop.en.txt b/lib/prompts/novel-promotion/select_prop.en.txt index de3d6c7..c7a6434 100644 --- a/lib/prompts/novel-promotion/select_prop.en.txt +++ b/lib/prompts/novel-promotion/select_prop.en.txt @@ -1,6 +1,6 @@ -You are a prop asset extractor. +You are a key story prop extractor. -Task: identify reusable physical props from the input text and return JSON only. +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. Output format: { @@ -12,16 +12,38 @@ Output format: ] } -Rules: -1. Only include concrete reusable physical props that actually appear in the story. -2. Only output `name` and `summary`. -3. `name` and `summary` must both be non-empty. -4. Do not repeat props that already exist in the prop library with the exact same name. -5. Exclude abstract concepts, powers, roles, places, creatures, outfits, and makeup. -6. Keep names stable and short. -7. Keep summaries objective. -8. If none exist, return {"props": []}. -9. Replace raw quotation marks inside JSON string values with corner brackets「」. +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: + - 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: +1. Ordinary background items, furniture, tableware, food, drinks, daily necessities, and decorations. +2. Objects that are only mentioned in passing and have no story function. +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. + +Decision bias: +1. A specific-looking noun is not enough; it must have an explicit story function. +2. If an object could be either a background item or a prop, treat it as background and do not output it. +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. + +Output rules: +1. Only output `name` and `summary`. +2. `name` and `summary` must both be non-empty. +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": []}. +8. Replace raw quotation marks inside JSON string values with corner brackets「」. Input: {input} diff --git a/lib/prompts/novel-promotion/select_prop.zh.txt b/lib/prompts/novel-promotion/select_prop.zh.txt index 90032c9..157c92f 100644 --- a/lib/prompts/novel-promotion/select_prop.zh.txt +++ b/lib/prompts/novel-promotion/select_prop.zh.txt @@ -1,6 +1,6 @@ -你是“故事道具资产分析师”。 +你是“关键剧情道具资产分析师”。 -任务:从输入文本中识别适合做成长期复用资产的道具,只返回 JSON,不得包含任何额外解释或 markdown。 +任务:从输入文本中只识别【关键道具】,用于建立需要长期保持外观一致的资产库。宁缺毋滥。只返回 JSON,不得包含任何额外解释或 markdown。 输出格式: { @@ -12,16 +12,38 @@ ] } -规则: -1. 只保留在剧情中真实出现、可被反复引用、值得进入资产库的实体道具。 -2. 只输出两个字段:name、summary。 -3. name 不能为空;summary 不能为空。 -4. 如果道具库里已经有完全同名道具,不要重复输出。 -5. 禁止输出抽象概念、情绪、能力、身份、地点、生物、服装妆容。 -6. 名称尽量简洁稳定,例如“青铜匕首”“录音笔”“红绳手链”。 -7. summary 只写客观描述,不写剧情推断。 -8. 如果没有合适道具,返回 {"props": []}。 -9. JSON 字符串值中的引号统一替换为「」。 +关键道具判定标准: +1. 必须是剧情中真实出现的实体物件。 +2. 必须在剧情中承担明确功能,而不只是背景摆设。 +3. 必须至少满足以下一种情况: + - 被角色持有、使用、争夺、交付、隐藏、丢失、寻找 + - 是推进情节的关键工具、武器、法器、证物、信物、钥匙、线索载体 + - 后续大概率需要重复出镜,且需要保持外观一致 + - 去掉它会明显影响剧情理解或关键动作成立 + +严格不提取: +1. 普通背景陈设、家具、餐具、食物、饮料、日用品、装饰物。 +2. 仅被顺带提及、没有剧情功能的物件。 +3. 场景自带的环境元素,除非它被明确当作关键道具使用。 +4. 普通服装、妆容、饰品,除非它本身就是关键线索或关键信物。 +5. 抽象概念、情绪、能力、身份、地点、生物、身体部位。 + +判断倾向: +1. 仅因外观具体、名词明确,不足以成为关键道具;必须有明确剧情作用。 +2. 如果一个物件既可能是背景物,也可能是道具,默认按背景物处理,不输出。 +3. 如果只是“出现过”,但没有“被使用/被强调/影响剧情”,不输出。 +4. 如果不确定它是否值得进入资产库,直接不输出。 +5. 优先少报,禁止为了凑数量而输出。 + +输出要求: +1. 只输出两个字段:name、summary。 +2. name 不能为空;summary 不能为空。 +3. 如果道具库里已经有完全同名道具,不要重复输出。 +4. 名称尽量简洁稳定,例如“青铜匕首”“录音笔”“红绳手链”。 +5. summary 只写客观描述,不写剧情推断。 +6. 通常不超过 3-5 个;只有确实都是关键道具时才可更多。 +7. 如果没有合适道具,返回 {"props": []}。 +8. JSON 字符串值中的引号统一替换为「」。 输入文本: {input} diff --git a/messages/en/assets.json b/messages/en/assets.json index d170ed3..af780ec 100644 --- a/messages/en/assets.json +++ b/messages/en/assets.json @@ -12,6 +12,8 @@ "confirmProfiles": "Character Profiles to Confirm", "confirmHint": "Please confirm these profiles before generating descriptions", "confirmAll": "Confirm All ({count})", + "pendingProfilesBanner": "AI Casting Complete", + "pendingProfilesHint": "Confirm profiles to auto-generate character visuals", "assetsTitle": "Asset Analysis", "characterAssets": "Character Assets", "locationAssets": "Location Assets", diff --git a/messages/en/progress.json b/messages/en/progress.json index 53a9353..0b2e2ed 100644 --- a/messages/en/progress.json +++ b/messages/en/progress.json @@ -125,6 +125,7 @@ "streamStep": { "analyzeCharacters": "Analyze characters", "analyzeLocations": "Analyze locations", + "analyzeProps": "Analyze props", "splitClips": "Split clips", "screenplayConversion": "Convert screenplay", "storyboardPlan": "Plan storyboard", diff --git a/messages/zh/assets.json b/messages/zh/assets.json index 576ed60..a6a7748 100644 --- a/messages/zh/assets.json +++ b/messages/zh/assets.json @@ -12,6 +12,8 @@ "confirmProfiles": "角色档案待确认", "confirmHint": "请确认以下角色档案后生成外貌描述", "confirmAll": "全部确认 ({count})", + "pendingProfilesBanner": "AI 选角完成", + "pendingProfilesHint": "确认档案后自动生成角色形象", "assetsTitle": "资产分析", "characterAssets": "角色资产", "locationAssets": "场景资产", diff --git a/messages/zh/progress.json b/messages/zh/progress.json index 7898230..5010522 100644 --- a/messages/zh/progress.json +++ b/messages/zh/progress.json @@ -125,6 +125,7 @@ "streamStep": { "analyzeCharacters": "角色分析", "analyzeLocations": "场景分析", + "analyzeProps": "道具分析", "splitClips": "片段切分", "screenplayConversion": "剧本转换", "storyboardPlan": "分镜规划", diff --git a/package-lock.json b/package-lock.json index d91ee0e..80ae2fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "cos-nodejs-sdk-v5": "^2.15.4", "express": "^5.2.1", "file-saver": "^2.0.5", + "geist": "^1.7.0", "ioredis": "^5.9.2", "jsonrepair": "^3.13.2", "jszip": "^3.10.1", @@ -10207,6 +10208,15 @@ "node": ">=18" } }, + "node_modules/geist": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/geist/-/geist-1.7.0.tgz", + "integrity": "sha512-ZaoiZwkSf0DwwB1ncdLKp+ggAldqxl5L1+SXaNIBGkPAqcu+xjVJLxlf3/S8vLt9UHx1xu5fz3lbzKCj5iOVdQ==", + "license": "SIL OPEN FONT LICENSE", + "peerDependencies": { + "next": ">=13.2.0" + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz", diff --git a/package.json b/package.json index af8f596..c2d46b0 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,7 @@ "cos-nodejs-sdk-v5": "^2.15.4", "express": "^5.2.1", "file-saver": "^2.0.5", + "geist": "^1.7.0", "ioredis": "^5.9.2", "jsonrepair": "^3.13.2", "jszip": "^3.10.1", diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index 7feb586..d106be3 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import Script from "next/script"; -import { Geist, Geist_Mono } from "next/font/google"; +import { GeistSans } from 'geist/font/sans'; +import { GeistMono } from 'geist/font/mono'; import { NextIntlClientProvider } from 'next-intl'; import { getMessages, getTranslations } from 'next-intl/server'; import { notFound } from 'next/navigation'; @@ -9,16 +10,6 @@ import { Providers } from "./providers"; import { locales } from '@/i18n/routing'; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - type SupportedLocale = (typeof locales)[number] @@ -72,7 +63,7 @@ export default async function LocaleLayout({ )}