统一SFX提示词 trim 规范化

将 SFX Prompt 首尾规范化改为 ECMAScript String.trim 语义并同步 Rust 等值实现

更新共享边界 fixture、前后端与 Worker 回归测试

同步权威设计、实施计划、决策日志和 External OpenAPI 描述
This commit is contained in:
2026-08-07 07:27:52 +00:00
parent 392741215c
commit 5c316fc680
17 changed files with 89 additions and 60 deletions
@@ -991,6 +991,7 @@ describe('ImageCanvasEditorModel', () => {
{ ...soundEffect, schemaVersion: 1 },
{ ...soundEffect, model: 'audio1.0' },
{ ...soundEffect, userPrompt: ' 金币落地' },
{ ...soundEffect, userPrompt: '\uFEFF金币落地,轻快可爱' },
{ ...soundEffect, actualDurationSeconds: 600.1 },
{
...soundEffect,
@@ -248,15 +248,15 @@ describe('ImageCanvasSoundEffectPresetModel', () => {
]);
});
it('canonicalizes and appends with the frozen Unicode punctuation rule', () => {
it('canonicalizes and appends with the frozen ECMAScript trim rule', () => {
const preset = SOUND_EFFECT_PROMPT_PRESETS[40];
expect(appendSoundEffectPromptPreset('\u0085\u2003', preset)).toBe(
expect(appendSoundEffectPromptPreset('\uFEFF\u2003', preset)).toBe(
preset.prompt,
);
expect(appendSoundEffectPromptPreset('金币落地', preset)).toBe(
`金币落地,${preset.prompt}`,
);
expect(appendSoundEffectPromptPreset('金币落地!\u0085', preset)).toBe(
expect(appendSoundEffectPromptPreset('金币落地!\uFEFF', preset)).toBe(
`金币落地!${preset.prompt}`,
);
});
@@ -39,7 +39,7 @@ describe('ImageCanvasSoundEffectPromptModel', () => {
});
it('accepts 2048 code points and rejects 2049 without truncating', () => {
const maximum = `\u0085${'😀'.repeat(
const maximum = `\uFEFF${'😀'.repeat(
SOUND_EFFECT_PROMPT_MAX_CODE_POINTS,
)}\u2003`;
const accepted = validateSoundEffectPrompt(maximum);
@@ -59,7 +59,7 @@ describe('ImageCanvasSoundEffectPromptModel', () => {
});
it('rejects empty canonical prompts without adding a default description', () => {
for (const prompt of ['', ' \t\r\n', '\u0085\u2003\u00a0']) {
for (const prompt of ['', ' \t\r\n', '\uFEFF\u2003\u00a0']) {
expect(validateSoundEffectPrompt(prompt)).toEqual({
ok: false,
prompt: '',
@@ -74,7 +74,7 @@ describe('ImageCanvasSoundEffectPromptModel', () => {
const started = model.beginOperation({
dialogId: 'dialog-a',
status: 'optimizing',
prompt: '\u0085金币落地声\u3000',
prompt: '\uFEFF金币落地声\u3000',
});
expect(started.started).toBe(true);
expect(started.canonicalPrompt).toBe('金币落地声');
@@ -6,9 +6,6 @@ import type {
export const SOUND_EFFECT_PROMPT_MAX_CODE_POINTS = 2048;
const SOUND_EFFECT_PROMPT_OPERATION_ID_PREFIX = 'sound-effect-prompt-operation';
const LEADING_UNICODE_WHITE_SPACE = /^\p{White_Space}+/u;
const TRAILING_UNICODE_WHITE_SPACE = /\p{White_Space}+$/u;
export type SoundEffectPromptValidationResult =
| {
ok: true;
@@ -57,13 +54,11 @@ export function toSoundEffectGenerationDialog(
}
/**
* 只删除首尾 Unicode `White_Space`。内部空白、组合字符、ZWJ、U+200B、U+FEFF
* 及其它 code point 均原样保留;不能替换为语义不同的原生 `trim()`。
* 按 JavaScript `String.trim()` 删除首尾 ECMAScript 空白和行终止符。内部空白、组合字符、
* ZWJ、U+200B 及其它 code point 均原样保留;首尾 U+FEFF 会被删除,U+0085 则保留。
*/
export function canonicalizeSoundEffectPrompt(prompt: string) {
return prompt
.replace(LEADING_UNICODE_WHITE_SPACE, '')
.replace(TRAILING_UNICODE_WHITE_SPACE, '');
return prompt.trim();
}
export function countSoundEffectPromptCodePoints(prompt: string) {
@@ -1938,7 +1938,7 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
initialDialog={{
id: 'dialog-sound',
mode: 'audio-sound-effect',
prompt: '\u0085\uFEFF金币掉落叮当声\uFEFF\u0085',
prompt: '\uFEFF金币掉落叮当声\uFEFF',
status: 'idle',
composerOpen: true,
soundDurationSeconds: 7,
@@ -1960,7 +1960,7 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
await waitFor(() => {
expect(generateEditorSoundEffectMock).toHaveBeenCalledWith(
expect.objectContaining({
prompt: '\uFEFF金币掉落叮当声\uFEFF',
prompt: '金币掉落叮当声',
duration: 7,
projectId: 'editor-project-audio',
assetFolderId: 'project',
@@ -1999,7 +1999,7 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
initialDialog={{
id: 'dialog-sound-empty',
mode: 'audio-sound-effect',
prompt: '\u0085 \n\t\u0085',
prompt: '\uFEFF \n\t\uFEFF',
status: 'idle',
composerOpen: true,
}}
@@ -2031,7 +2031,7 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
initialDialog={{
id: 'dialog-sfx-claim',
mode: 'audio-sound-effect',
prompt: '\u0085循环环境声\u3000',
prompt: '\uFEFF循环环境声\u3000',
status: 'idle',
composerOpen: true,
soundModel: 'eleven_text_to_sound_v2',
@@ -148,7 +148,7 @@ describe('useImageCanvasSoundEffectPromptAssist', () => {
const { result } = renderHook(() =>
useHarness({
initialDialogs: [
createDialog('dialog-a', '\u0085金币落地声\u3000', {
createDialog('dialog-a', '\uFEFF金币落地声\u3000', {
status: 'failed',
errorMessage: '旧错误',
}),
@@ -319,7 +319,7 @@ describe('useImageCanvasSoundEffectPromptAssist', () => {
const { result } = renderHook(() =>
useHarness({
initialDialogs: [
createDialog('dialog-a', ' \u0085循环环境声\u3000', {
createDialog('dialog-a', ' \uFEFF循环环境声\u3000', {
soundDurationMode: 'auto',
soundDurationSeconds: 7.3,
soundLoop: true,