diff --git a/scripts/dev-api.js b/scripts/dev-api.js index 2c47f03..42453cb 100644 --- a/scripts/dev-api.js +++ b/scripts/dev-api.js @@ -3599,6 +3599,13 @@ function normalizeHermesDisplayToolProgress(value, strict = false, key = 'displa return 'all' } +function normalizeHermesDisplayToolPrefix(value, strict = false) { + const prefix = String(value ?? '').trim() || '┊' + if (prefix.length <= 8 && !/[\r\n\t]/.test(prefix)) return prefix + if (strict) throw new Error('display.tool_prefix 必须是 1 到 8 个字符,且不能包含换行或制表符') + return '┊' +} + function normalizeHermesDisplayStreaming(value, strict = false, key = 'display.streaming') { if (typeof value === 'boolean') return value ? 'true' : 'false' const streaming = String(value ?? '').trim().toLowerCase() || 'inherit' @@ -3687,6 +3694,7 @@ export function buildHermesDisplayConfigValues(config = {}) { return { displayCompact: readHermesBool(display.compact, false), displaySkin: normalizeHermesDisplaySkin(display.skin, false), + displayToolPrefix: normalizeHermesDisplayToolPrefix(display.tool_prefix, false), displayToolProgress: normalizeHermesDisplayToolProgress(display.tool_progress, false), displayShowReasoning: readHermesBool(display.show_reasoning, false), displayToolPreviewLength: parseHermesInteger(display.tool_preview_length, 'display.tool_preview_length', 0, 0, 200000, false), @@ -3720,6 +3728,7 @@ export function mergeHermesDisplayConfig(config = {}, form = {}) { display.compact = formHermesBool(form, 'displayCompact', currentValues.displayCompact) display.skin = normalizeHermesDisplaySkin(Object.hasOwn(form, 'displaySkin') ? form.displaySkin : currentValues.displaySkin, true) + display.tool_prefix = normalizeHermesDisplayToolPrefix(Object.hasOwn(form, 'displayToolPrefix') ? form.displayToolPrefix : currentValues.displayToolPrefix, true) display.tool_progress = normalizeHermesDisplayToolProgress(Object.hasOwn(form, 'displayToolProgress') ? form.displayToolProgress : currentValues.displayToolProgress, true, 'display.tool_progress') display.show_reasoning = formHermesBool(form, 'displayShowReasoning', currentValues.displayShowReasoning) display.tool_preview_length = parseHermesInteger(Object.hasOwn(form, 'displayToolPreviewLength') ? form.displayToolPreviewLength : currentValues.displayToolPreviewLength, 'display.tool_preview_length', 0, 0, 200000, true) diff --git a/src-tauri/src/commands/hermes.rs b/src-tauri/src/commands/hermes.rs index 4a1cf2d..4649c4c 100644 --- a/src-tauri/src/commands/hermes.rs +++ b/src-tauri/src/commands/hermes.rs @@ -2209,6 +2209,25 @@ fn normalize_hermes_display_tool_progress( } } +fn normalize_hermes_display_tool_prefix( + value: Option, + strict: bool, +) -> Result { + let prefix = value.unwrap_or_default().trim().to_string(); + let prefix = if prefix.is_empty() { + "┊".to_string() + } else { + prefix + }; + if prefix.chars().count() <= 8 && !prefix.contains(['\r', '\n', '\t']) { + Ok(prefix) + } else if strict { + Err("display.tool_prefix 必须是 1 到 8 个字符,且不能包含换行或制表符".to_string()) + } else { + Ok("┊".to_string()) + } +} + fn normalize_hermes_display_streaming_text( value: Option, strict: bool, @@ -6232,6 +6251,10 @@ fn build_hermes_display_config_values(config: &serde_yaml::Value) -> Value { display.and_then(|map| yaml_string_field(map, "skin")), false, ).unwrap_or_else(|_| "default".to_string()), + "displayToolPrefix": normalize_hermes_display_tool_prefix( + display.and_then(|map| yaml_string_field(map, "tool_prefix")), + false, + ).unwrap_or_else(|_| "┊".to_string()), "displayToolProgress": normalize_hermes_display_tool_progress( display.and_then(|map| yaml_string_field(map, "tool_progress")), false, @@ -6338,6 +6361,17 @@ fn merge_hermes_display_config(config: &mut serde_yaml::Value, form: &Value) -> true, )?), ); + display.insert( + yaml_key("tool_prefix"), + serde_yaml::Value::String(normalize_hermes_display_tool_prefix( + form_string(form, "displayToolPrefix").or_else(|| { + current["displayToolPrefix"] + .as_str() + .map(ToString::to_string) + }), + true, + )?), + ); display.insert( yaml_key("tool_progress"), serde_yaml::Value::String(tool_progress), @@ -18958,6 +18992,7 @@ mod hermes_display_config_tests { assert_eq!(values["displayToolProgress"], "all"); assert_eq!(values["displayCompact"], false); assert_eq!(values["displaySkin"], "default"); + assert_eq!(values["displayToolPrefix"], "┊"); assert_eq!(values["displayShowReasoning"], false); assert_eq!(values["displayToolPreviewLength"], 0); assert_eq!(values["displayCleanupProgress"], false); @@ -18988,6 +19023,7 @@ display: tool_progress: VERBOSE compact: true skin: MONO + tool_prefix: "╎" show_reasoning: true tool_preview_length: 80 cleanup_progress: true @@ -19016,6 +19052,7 @@ display: assert_eq!(values["displayToolProgress"], "verbose"); assert_eq!(values["displayCompact"], true); assert_eq!(values["displaySkin"], "mono"); + assert_eq!(values["displayToolPrefix"], "╎"); assert_eq!(values["displayShowReasoning"], true); assert_eq!(values["displayToolPreviewLength"], 80); assert_eq!(values["displayCleanupProgress"], true); @@ -19064,6 +19101,7 @@ memory: "displayToolProgress": "off", "displayCompact": true, "displaySkin": "slate", + "displayToolPrefix": "│", "displayShowReasoning": true, "displayToolPreviewLength": 120, "displayCleanupProgress": true, @@ -19089,6 +19127,7 @@ memory: assert_eq!(config["memory"]["memory_enabled"].as_bool(), Some(true)); assert_eq!(config["display"]["compact"].as_bool(), Some(true)); assert_eq!(config["display"]["skin"].as_str(), Some("slate")); + assert_eq!(config["display"]["tool_prefix"].as_str(), Some("│")); assert_eq!(config["display"]["show_reasoning"].as_bool(), Some(true)); assert_eq!(config["display"]["tool_preview_length"].as_i64(), Some(120)); assert_eq!(config["display"]["cleanup_progress"].as_bool(), Some(true)); @@ -19166,6 +19205,13 @@ memory: .unwrap_err(); assert!(err.contains("display.skin")); + let err = merge_hermes_display_config( + &mut config, + &json!({ "displayToolPrefix": "too-long-prefix" }), + ) + .unwrap_err(); + assert!(err.contains("display.tool_prefix")); + let err = merge_hermes_display_config(&mut config, &json!({ "displayResumeDisplay": "compact" })) .unwrap_err(); diff --git a/src/engines/hermes/pages/config.js b/src/engines/hermes/pages/config.js index 8575044..0746462 100644 --- a/src/engines/hermes/pages/config.js +++ b/src/engines/hermes/pages/config.js @@ -147,6 +147,7 @@ const DISPLAY_DEFAULTS = { displayToolProgress: 'all', displayCompact: false, displaySkin: 'default', + displayToolPrefix: '┊', displayShowReasoning: false, displayToolPreviewLength: 0, displayCleanupProgress: false, @@ -1327,6 +1328,10 @@ export function render() { ${DISPLAY_SKINS.map(mode => option(`engine.hermesDisplayConfigSkin_${mode}`, mode, displayValues.displaySkin)).join('')} +