using System.Reflection; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using AIStudio.Tools.Metadata; using HtmlAgilityPack; namespace AIStudio.Assistants.VisualBriefing; /// /// Defines VisualBriefingArtifactService for the visual briefing feature. /// public sealed partial class VisualBriefingArtifactService { /// /// Marks the Base64 artifact header embedded at the start of standalone HTML. /// private const string HEADER_MARKER = "MWAI_VISUAL_BRIEFING_HEADER:"; /// /// Breaks the circular dependency while hashing a document that carries its own hash. /// private const string DOCUMENT_HASH_PLACEHOLDER = "0000000000000000000000000000000000000000000000000000000000000000"; /// /// Identifies the canonical JSON script element. /// private const string DATA_ELEMENT_ID = "mwai-briefing-data"; /// /// Gets the frozen JSON configuration whose bytes the document hash covers. /// private static readonly JsonSerializerOptions JSON_OPTIONS = VisualBriefingJson.Canonical; /// /// Defines HtmlLanguageTagRegex for the visual briefing feature. /// private static readonly Regex HTML_LANGUAGE_TAG = HtmlLanguageTagRegex(); /// /// Lazily loads the pinned ECharts common distribution. /// private static readonly Lazy ECHARTS_SCRIPT = new(LoadECharts); /// /// Defines AIStudioVersion for the visual briefing feature. /// private string AIStudioVersion { get; } = Assembly.GetExecutingAssembly().GetCustomAttribute()?.Version ?? "unknown"; /// /// Defines RuntimeScript for the visual briefing feature. /// private string RuntimeScript => BuildRuntimeScript(this.AIStudioVersion); /// /// Defines NormalizeTemplate for the visual briefing feature. /// private static string NormalizeTemplate(string template) => template.Trim().Replace("\r\n", "\n", StringComparison.Ordinal); // HtmlAgilityPack's public annotations declare these lookup APIs as non-null even though // they return null for missing nodes and attributes. Keep that behavior explicit here. // ReSharper disable once ReturnTypeCanBeNotNullable /// /// Defines FindElementById for the visual briefing feature. /// private static HtmlNode? FindElementById(HtmlDocument document, string id) => document.GetElementbyId(id); // ReSharper disable once ReturnTypeCanBeNotNullable /// /// Defines FindNode for the visual briefing feature. /// private static HtmlNode? FindNode(HtmlNode node, string xpath) => node.SelectSingleNode(xpath); // ReSharper disable once ReturnTypeCanBeNotNullable /// /// Defines FindNodes for the visual briefing feature. /// private static HtmlNodeCollection? FindNodes(HtmlNode node, string xpath) => node.SelectNodes(xpath); // ReSharper disable once ReturnTypeCanBeNotNullable /// /// Defines FindAttribute for the visual briefing feature. /// private static HtmlAttribute? FindAttribute(HtmlNode node, string name) => node.Attributes[name]; /// /// Defines CanonicalizeTemplate for the visual briefing feature. /// private static string CanonicalizeTemplate(string template) { var document = new HtmlDocument(); document.LoadHtml($"
{NormalizeTemplate(template)}
"); return NormalizeTemplate(FindElementById(document, "mwai-canonical-root")?.InnerHtml ?? string.Empty); } /// /// Defines GetHtmlLanguage for the visual briefing feature. /// private static string GetHtmlLanguage(CommonLanguages language, string customLanguage) => language switch { CommonLanguages.DE_DE => "de-DE", CommonLanguages.DE_AT => "de-AT", CommonLanguages.DE_CH => "de-CH", CommonLanguages.ZH_CN => "zh-CN", CommonLanguages.HI_IN => "hi-IN", CommonLanguages.ES_ES => "es-ES", CommonLanguages.FR_FR => "fr-FR", CommonLanguages.JA_JP => "ja-JP", CommonLanguages.RU_RU => "ru-RU", CommonLanguages.EN_GB => "en-GB", CommonLanguages.EN_US => "en-US", CommonLanguages.OTHER when HTML_LANGUAGE_TAG.IsMatch(customLanguage.Trim()) => customLanguage.Trim(), _ => "und", }; /// /// Defines LoadECharts for the visual briefing feature. /// private static string? LoadECharts() { var assembly = Assembly.GetExecutingAssembly(); var resourceName = assembly.GetManifestResourceNames() .FirstOrDefault(name => name.EndsWith("Assistants.VisualBriefing.Runtime.echarts.common.min.js", StringComparison.Ordinal)); if (resourceName is null) return null; using var stream = assembly.GetManifestResourceStream(resourceName); if (stream is null) return null; using var reader = new StreamReader(stream, Encoding.UTF8); return reader.ReadToEnd(); } /// /// Defines HtmlLanguageTagRegex for the visual briefing feature. /// [GeneratedRegex(@"^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$", RegexOptions.CultureInvariant)] private static partial Regex HtmlLanguageTagRegex(); }