diff --git a/mateclaw-ui/src/components/workflow/StepPropertyPanel.vue b/mateclaw-ui/src/components/workflow/StepPropertyPanel.vue index 6f6b77b8..73e58964 100644 --- a/mateclaw-ui/src/components/workflow/StepPropertyPanel.vue +++ b/mateclaw-ui/src/components/workflow/StepPropertyPanel.vue @@ -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" /> @@ -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(() => { diff --git a/mateclaw-ui/src/i18n/index.ts b/mateclaw-ui/src/i18n/index.ts index 02c87c76..46d5ba3f 100644 --- a/mateclaw-ui/src/i18n/index.ts +++ b/mateclaw-ui/src/i18n/index.ts @@ -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(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, + messageCompiler: safeMessageCompiler, }) const loadedLocales = new Set() diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index 3d55391f..eb68d153 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -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', diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index 7018e49e..2b03cc8a 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -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: '输出类型',