Files
Genarrative/src/components/image-editor/ImageCanvasBackgroundMusicPresetModel.ts
T
lhk229 281c84b7bf
Project CI / Repository checks (push) Successful in 1m15s
Project CI / Frontend tests (push) Failing after 2m11s
Project CI / Backend tests (push) Successful in 3m33s
Project CI / Native shell tests (push) Successful in 13m57s
BGM生成路径优化 (#142)
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/142
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-08-06 17:54:40 +08:00

244 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { canonicalizeBackgroundMusicPrompt } from './ImageCanvasBackgroundMusicPromptModel';
export type BackgroundMusicPromptPresetGroup =
| 'purpose'
| 'atmosphere'
| 'scene';
export type BackgroundMusicPromptPreset = {
id: string;
group: BackgroundMusicPromptPresetGroup;
label: string;
prompt: string;
};
const UNICODE_PUNCTUATION_CODE_POINT = /^\p{Punctuation}$/u;
const BACKGROUND_MUSIC_PRESET_SENTENCE_SEPARATOR = '。';
export const BACKGROUND_MUSIC_PROMPT_PRESET_GROUPS = [
'purpose',
'atmosphere',
'scene',
] as const satisfies readonly BackgroundMusicPromptPresetGroup[];
/**
* 权威设计《画板音乐生成入口设计》「预设库与追加规则」的固定文案,逐项复制,不改写。
*
* `id`、`group` 只用于滚动轨道的颜色分组和 React key`label` 只用于 chip 展示;
* 写入输入框的永远只有 `prompt`,隐藏元数据不得进入 Prompt 或 Suno 请求。
*/
export const BACKGROUND_MUSIC_PROMPT_PRESETS = [
{
id: 'menu-idle',
group: 'purpose',
label: '菜单待机',
prompt: '低干扰、适合菜单待机的背景音乐',
},
{
id: 'casual-match',
group: 'purpose',
label: '休闲消除',
prompt: '轻快可爱的休闲消除背景音乐',
},
{
id: 'puzzle-thinking',
group: 'purpose',
label: '解谜思考',
prompt: '安静专注的解谜思考背景音乐',
},
{
id: 'exploration-adventure',
group: 'purpose',
label: '探索冒险',
prompt: '温和推进的探索冒险背景音乐',
},
{
id: 'dialogue-scene',
group: 'purpose',
label: '对白场景',
prompt: '克制柔和、留出对白空间的背景音乐',
},
{
id: 'pre-battle',
group: 'purpose',
label: '战斗前',
prompt: '蓄势待发的战斗前背景音乐',
},
{
id: 'victory-settlement',
group: 'purpose',
label: '胜利结算',
prompt: '明亮满足的胜利结算背景音乐',
},
{
id: 'defeat-settlement',
group: 'purpose',
label: '失败结算',
prompt: '克制低落的失败结算背景音乐',
},
{
id: 'daily-management',
group: 'purpose',
label: '日常经营',
prompt: '轻松有序的日常经营背景音乐',
},
{
id: 'farm-management',
group: 'purpose',
label: '农场经营',
prompt: '自然温暖的农场经营背景音乐',
},
{
id: 'campus-daily',
group: 'purpose',
label: '校园日常',
prompt: '青春轻松的校园日常背景音乐',
},
{
id: 'food-kitchen',
group: 'purpose',
label: '美食厨房',
prompt: '温暖活泼的美食厨房背景音乐',
},
{
id: 'warm-healing',
group: 'atmosphere',
label: '温暖治愈',
prompt: '柔和明亮的治愈背景音乐',
},
{
id: 'mystery-suspense',
group: 'atmosphere',
label: '神秘悬疑',
prompt: '克制神秘的悬疑背景音乐',
},
{
id: 'tension-buildup',
group: 'atmosphere',
label: '紧张推进',
prompt: '稳定推进、逐渐紧张的背景音乐',
},
{
id: 'forest-nature',
group: 'scene',
label: '森林自然',
prompt: '清新自然的森林背景音乐',
},
{
id: 'rainy-night',
group: 'scene',
label: '雨夜静谧',
prompt: '雨夜静谧、略带神秘感的背景音乐',
},
{
id: 'ocean-drift',
group: 'scene',
label: '海洋漂流',
prompt: '开阔舒缓的海洋漂流背景音乐',
},
{
id: 'mountain-journey',
group: 'scene',
label: '山野远行',
prompt: '自由舒展的山野远行背景音乐',
},
{
id: 'candy-park',
group: 'scene',
label: '糖果乐园',
prompt: '甜美活泼的糖果乐园背景音乐',
},
{
id: 'cozy-cabin',
group: 'scene',
label: '温馨小屋',
prompt: '温暖安静的温馨小屋背景音乐',
},
{
id: 'city-night',
group: 'scene',
label: '城市夜晚',
prompt: '克制迷人的城市夜晚背景音乐',
},
{
id: 'space-scifi',
group: 'scene',
label: '太空科幻',
prompt: '空灵未来感的太空科幻背景音乐',
},
{
id: 'cyber-block',
group: 'scene',
label: '赛博街区',
prompt: '冷静律动的赛博街区背景音乐',
},
{
id: 'ancient-fantasy',
group: 'scene',
label: '古风幻想',
prompt: '空灵雅致的古风幻想背景音乐',
},
{
id: 'fairytale-garden',
group: 'scene',
label: '童话花园',
prompt: '梦幻轻盈的童话花园背景音乐',
},
{
id: 'undersea-ruins',
group: 'scene',
label: '海底遗迹',
prompt: '深邃神秘的海底遗迹背景音乐',
},
{
id: 'desert-ruins',
group: 'scene',
label: '沙漠遗迹',
prompt: '苍茫神秘的沙漠遗迹背景音乐',
},
{
id: 'lava-cave',
group: 'scene',
label: '熔岩洞穴',
prompt: '炽热压迫的熔岩洞穴背景音乐',
},
{
id: 'curious-museum',
group: 'scene',
label: '奇妙博物馆',
prompt: '好奇灵动的奇妙博物馆背景音乐',
},
] as const satisfies readonly BackgroundMusicPromptPreset[];
function readLastCodePoint(value: string) {
// 必须按 code point 读取:直接取最后一个 UTF-16 code unit 会在 emoji 等
// surrogate pair 结尾时拿到半个字符,标点判断随之失真。
const codePoints = Array.from(value);
return codePoints[codePoints.length - 1];
}
/**
* 按权威设计的追加规则返回新的可见 Prompt。
*
* 输入先复用共享 canonicalizer(不使用会额外删除 U+FEFF 的原生 `trim()`),因此调用方
* 传入尚未规范化的文本也不会产出 ` 。预设文本` 这类结果;canonicalization 幂等,
* 已经写回过的文本再算一次结果相同。
*
* 不去重、不截断、不改内部空格与换行;追加后允许超过 200 或 2000 字,文本必须完整保留。
*/
export function appendBackgroundMusicPromptPreset(
currentPrompt: string,
preset: BackgroundMusicPromptPreset,
) {
const canonicalPrompt = canonicalizeBackgroundMusicPrompt(currentPrompt);
if (!canonicalPrompt) {
return preset.prompt;
}
const lastCodePoint = readLastCodePoint(canonicalPrompt);
if (lastCodePoint && UNICODE_PUNCTUATION_CODE_POINT.test(lastCodePoint)) {
return `${canonicalPrompt}${preset.prompt}`;
}
return `${canonicalPrompt}${BACKGROUND_MUSIC_PRESET_SENTENCE_SEPARATOR}${preset.prompt}`;
}