diff --git a/src/OpenClaw.Tray.WinUI/Controls/SchemaConfigEditor.xaml.cs b/src/OpenClaw.Tray.WinUI/Controls/SchemaConfigEditor.xaml.cs index c5d69f18..596fefa5 100644 --- a/src/OpenClaw.Tray.WinUI/Controls/SchemaConfigEditor.xaml.cs +++ b/src/OpenClaw.Tray.WinUI/Controls/SchemaConfigEditor.xaml.cs @@ -17,6 +17,10 @@ public sealed partial class SchemaConfigEditor : UserControl private JsonElement _config; private readonly Dictionary _changes = new(); + private static readonly Regex CamelCaseSplitPattern = new( + "([a-z])([A-Z])", + RegexOptions.Compiled); + private static readonly SolidColorBrush SecondaryBrush = new(ColorHelper.FromArgb(255, 140, 150, 170)); @@ -378,7 +382,7 @@ private void UpdateArrayChanges(StackPanel itemsPanel, string path) private static string GetLabel(string path, string name) { - var result = Regex.Replace(name, "([a-z])([A-Z])", "$1 $2"); + var result = CamelCaseSplitPattern.Replace(name, "$1 $2"); result = result.Replace("_", " ").Replace(".", " \u203A "); // Title-case the first character if (result.Length > 0) diff --git a/src/OpenClaw.Tray.WinUI/Helpers/CommandCenterTextHelper.cs b/src/OpenClaw.Tray.WinUI/Helpers/CommandCenterTextHelper.cs index d2126c80..622759d7 100644 --- a/src/OpenClaw.Tray.WinUI/Helpers/CommandCenterTextHelper.cs +++ b/src/OpenClaw.Tray.WinUI/Helpers/CommandCenterTextHelper.cs @@ -11,6 +11,48 @@ namespace OpenClawTray.Helpers; internal static class CommandCenterTextHelper { + // Pre-compiled patterns used in RedactSupportPath / RedactSupportValue. + // Compiled once at startup; reused on every diagnostic / support-text build. + private static readonly Regex PathWindowsUserPattern = new( + @"\b[A-Za-z]:\\Users\\[^\\]+", + RegexOptions.IgnoreCase | RegexOptions.Compiled, + TimeSpan.FromMilliseconds(100)); + + private static readonly Regex PathUnixUserPattern = new( + @"/Users/[^/]+", + RegexOptions.Compiled, + TimeSpan.FromMilliseconds(100)); + + private static readonly Regex ValueUrlHostPattern = new( + @"\b[a-z][a-z0-9+.-]*://(?:[^@\s/]+@)?([^:/\s]+)", + RegexOptions.IgnoreCase | RegexOptions.Compiled, + TimeSpan.FromMilliseconds(100)); + + private static readonly Regex ValueIpPattern = new( + @"\b(?:\d{1,3}\.){3}\d{1,3}\b", + RegexOptions.Compiled, + TimeSpan.FromMilliseconds(100)); + + private static readonly Regex ValueEmailPattern = new( + @"\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b", + RegexOptions.IgnoreCase | RegexOptions.Compiled, + TimeSpan.FromMilliseconds(100)); + + private static readonly Regex ValueUserAtHostPattern = new( + @"\b(?[A-Za-z0-9._-]+)@(?[A-Za-z0-9._-]+)(?=[:\s]|$)", + RegexOptions.Compiled, + TimeSpan.FromMilliseconds(100)); + + private static readonly Regex ValueHostAfterToPattern = new( + @"(?<=\bto\s)[A-Za-z0-9._-]+(?=:\d{1,5}\b)", + RegexOptions.IgnoreCase | RegexOptions.Compiled, + TimeSpan.FromMilliseconds(100)); + + private static readonly Regex ValueLeadingHostPattern = new( + @"^\s*[A-Za-z0-9._-]+(?=:\d{1,5}\b)", + RegexOptions.Compiled, + TimeSpan.FromMilliseconds(100)); + internal static string BuildSupportContext(GatewayCommandCenterState state) { var builder = new StringBuilder(); @@ -346,19 +388,9 @@ private static string RedactSupportPath(string? path) } } - redacted = Regex.Replace( - redacted, - @"\b[A-Za-z]:\\Users\\[^\\]+", - "%USERPROFILE%", - RegexOptions.IgnoreCase, - TimeSpan.FromMilliseconds(100)); + redacted = PathWindowsUserPattern.Replace(redacted, "%USERPROFILE%"); - redacted = Regex.Replace( - redacted, - @"/Users/[^/]+", - "$HOME", - RegexOptions.None, - TimeSpan.FromMilliseconds(100)); + redacted = PathUnixUserPattern.Replace(redacted, "$HOME"); return redacted; } @@ -368,47 +400,19 @@ private static string RedactSupportValue(string? value) if (string.IsNullOrWhiteSpace(value)) return "unknown"; - var redacted = Regex.Replace( + var redacted = ValueUrlHostPattern.Replace( value, - @"\b[a-z][a-z0-9+.-]*://(?:[^@\s/]+@)?([^:/\s]+)", - match => match.Value.Replace(match.Groups[1].Value, ""), - RegexOptions.IgnoreCase, - TimeSpan.FromMilliseconds(100)); - - redacted = Regex.Replace( - redacted, - @"\b(?:\d{1,3}\.){3}\d{1,3}\b", - "", - RegexOptions.None, - TimeSpan.FromMilliseconds(100)); - - redacted = Regex.Replace( - redacted, - @"\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b", - "", - RegexOptions.IgnoreCase, - TimeSpan.FromMilliseconds(100)); - - redacted = Regex.Replace( - redacted, - @"\b(?[A-Za-z0-9._-]+)@(?[A-Za-z0-9._-]+)(?=[:\s]|$)", - "@", - RegexOptions.None, - TimeSpan.FromMilliseconds(100)); - - redacted = Regex.Replace( - redacted, - @"(?<=\bto\s)[A-Za-z0-9._-]+(?=:\d{1,5}\b)", - "", - RegexOptions.IgnoreCase, - TimeSpan.FromMilliseconds(100)); - - redacted = Regex.Replace( - redacted, - @"^\s*[A-Za-z0-9._-]+(?=:\d{1,5}\b)", - "", - RegexOptions.None, - TimeSpan.FromMilliseconds(100)); + match => match.Value.Replace(match.Groups[1].Value, "")); + + redacted = ValueIpPattern.Replace(redacted, ""); + + redacted = ValueEmailPattern.Replace(redacted, ""); + + redacted = ValueUserAtHostPattern.Replace(redacted, "@"); + + redacted = ValueHostAfterToPattern.Replace(redacted, ""); + + redacted = ValueLeadingHostPattern.Replace(redacted, ""); return redacted; } diff --git a/src/OpenClaw.Tray.WinUI/Onboarding/Pages/WizardPage.cs b/src/OpenClaw.Tray.WinUI/Onboarding/Pages/WizardPage.cs index 7355f1ec..64d28014 100644 --- a/src/OpenClaw.Tray.WinUI/Onboarding/Pages/WizardPage.cs +++ b/src/OpenClaw.Tray.WinUI/Onboarding/Pages/WizardPage.cs @@ -18,6 +18,13 @@ namespace OpenClawTray.Onboarding.Pages; /// public sealed class WizardPage : Component { + private static readonly Regex UrlInMessagePattern = new( + @"(https?://[^\s\)\"",]+)", + RegexOptions.Compiled); + + private static readonly Regex DeviceCodePattern = new( + @"(?:^|\s)(?:[Cc]ode|user_code|USER_CODE)\s*[:=]\s*([A-Z0-9]{2,8}(?:-[A-Z0-9]{2,8})+|[A-Z0-9]{4,12})\b", + RegexOptions.Compiled); public override Element Render() { // Read persisted wizard state from shared OnboardingState @@ -523,7 +530,7 @@ async void SkipStep() if (!string.IsNullOrEmpty(displayMessage)) { // URL detection — find https:// URLs in the message - var urlMatch = Regex.Match(displayMessage, @"(https?://[^\s\)\"",]+)"); + var urlMatch = UrlInMessagePattern.Match(displayMessage); if (urlMatch.Success) { var detectedUrl = urlMatch.Value; @@ -545,9 +552,7 @@ async void SkipStep() // Capture must contain a digit or hyphen (or be all uppercase) to avoid // matching common English words like "below" that follow "code". // Case-sensitive on the value to require the GitHub-style uppercase code. - var codeMatch = Regex.Match( - displayMessage, - @"(?:^|\s)(?:[Cc]ode|user_code|USER_CODE)\s*[:=]\s*([A-Z0-9]{2,8}(?:-[A-Z0-9]{2,8})+|[A-Z0-9]{4,12})\b"); + var codeMatch = DeviceCodePattern.Match(displayMessage); if (codeMatch.Success) { var code = codeMatch.Groups[1].Value; @@ -581,7 +586,7 @@ async void SkipStep() { if (!string.IsNullOrEmpty(displayMessage)) { - var urlMatch = Regex.Match(displayMessage, @"(https?://[^\s\)\"",]+)"); + var urlMatch = UrlInMessagePattern.Match(displayMessage); if (urlMatch.Success) { try