fix(ui/workflows): keep property panel rendering when an i18n message contains Pebble braces

This commit is contained in:
matevip 2026-05-13 20:15:12 +08:00
parent 4aad813f2d
commit 004df80611
4 changed files with 33 additions and 3 deletions

View File

@ -66,7 +66,7 @@
@input="patch({ promptTemplate: ($event.target as HTMLTextAreaElement).value })"
spellcheck="false"
rows="3"
:placeholder="t('workflows.canvas.fields.promptPlaceholder')"
:placeholder="PROMPT_PLACEHOLDER"
/>
</label>
@ -319,6 +319,14 @@ const emit = defineEmits<{
const { t } = useI18n()
// The Pebble example shown in the textarea placeholder is the same in every
// locale and contains literal `{{ }}`. Routing it through vue-i18n's t()
// works but the library does a second compile pass on the returned string
// looking for linked messages / nested placeholders, which trips on the
// inner braces and floods the console with parse errors. Keeping it as a
// plain const sidesteps the parser entirely.
const PROMPT_PLACEHOLDER = 'Hello {{ inputs.payload }}'
const modeType = computed(() => (props.step?.mode?.type ?? 'sequential') as string)
const localizedModeLabel = computed(() => {

View File

@ -1,4 +1,5 @@
import { createI18n } from 'vue-i18n'
import { compileToFunction, registerMessageCompiler } from '@intlify/core-base'
import { ref } from 'vue'
import { settingsApi } from '@/api'
@ -9,11 +10,30 @@ const DEFAULT_LOCALE: AppLocale = 'zh-CN'
export const currentLocale = ref<AppLocale>(DEFAULT_LOCALE)
// Replace vue-i18n's default message compiler with a safety wrapper. The
// default compiler throws on parse errors in production builds, which
// caused a regression: workflow step prompts containing Pebble syntax
// (`Hello {{ inputs.payload }}`) leaked into i18n's parser through one of
// vue-i18n's internal lookups and aborted the property panel render.
// Catching the throw here keeps the panel alive — the worst case is that
// a malformed message renders as its literal text instead of the
// interpolated form, which is the same fallback dev mode already has.
const safeMessageCompiler = ((message: any, context: any) => {
try {
return compileToFunction(message, context)
} catch {
const literal = typeof message === 'string' ? message : String(message)
return () => literal
}
}) as typeof compileToFunction
registerMessageCompiler(safeMessageCompiler)
export const i18n = createI18n({
legacy: false,
locale: DEFAULT_LOCALE,
fallbackLocale: DEFAULT_LOCALE,
messages: {} as Record<AppLocale, any>,
messageCompiler: safeMessageCompiler,
})
const loadedLocales = new Set<AppLocale>()

View File

@ -2222,7 +2222,8 @@ export default {
agentPlaceholder: 'Select digital employee',
agentMissing: 'Current digital employee is not in the list: {name}',
promptTemplate: 'Prompt template',
promptPlaceholder: 'Hello {{ inputs.payload }}',
// promptPlaceholder is intentionally not localized — hardcoded in
// StepPropertyPanel so vue-i18n's parser never sees the Pebble braces.
outputVar: 'Output variable',
outputVarPlaceholder: 'data',
outputContentType: 'Output type',

View File

@ -2234,7 +2234,8 @@ export default {
agentPlaceholder: '选择数字员工',
agentMissing: '当前数字员工不在列表中:{name}',
promptTemplate: 'Prompt 模板',
promptPlaceholder: 'Hello {{ inputs.payload }}',
// promptPlaceholder is intentionally not localized — hardcoded in
// StepPropertyPanel so vue-i18n's parser never sees the Pebble braces.
outputVar: '输出变量名',
outputVarPlaceholder: 'data',
outputContentType: '输出类型',