#nullable enable namespace T3.Editor.Gui.Help; /// /// Identifies a documentation topic shown in the : either an operator /// (by symbol id) or a ui: UI topic (by its help-index key). Value-equality makes it usable as a /// history entry. /// internal readonly record struct HelpTopic(Guid SymbolId, string? UiTopicKey) { internal static HelpTopic ForOperator(Guid symbolId) => new(symbolId, null); /// Accepts both a full ui:Id key and a bare id; normalizes so both forms dedupe in history. internal static HelpTopic ForUiTopic(string keyOrId) { var key = keyOrId.StartsWith(UiPrefix, StringComparison.Ordinal) ? keyOrId : UiPrefix + keyOrId; return new HelpTopic(Guid.Empty, key); } internal bool IsEmpty => SymbolId == Guid.Empty && UiTopicKey == null; private const string UiPrefix = "ui:"; } /// /// Resolves a ui: topic to its display title and markdown doc. A bespoke /// .help/embedded/<id>.md snippet wins over the topic registry's doc, so a documentation /// icon can carry hand-written content while still falling back to the shared ui: topic body. /// internal static class UiTopicDocs { /// /// True if the id names a known topic (registry entry or embedded snippet); /// may still be null for a registry topic whose doc hasn't been written yet. /// internal static bool TryGet(string keyOrId, out string title, out string? markdown) { var key = keyOrId.StartsWith("ui:", StringComparison.Ordinal) ? keyOrId : "ui:" + keyOrId; if (_cache.TryGetValue(key, out var cached)) { title = cached.Title; markdown = cached.Doc; return cached.Found; } var id = key.Substring(3); var doc = EmbeddedHelpLoader.TryLoad(id); var resolvedTitle = id; var found = doc != null; if (HelpIndex.TryGetTopic(key, out var topic) && topic != null) { resolvedTitle = topic.Term; found = true; if (doc == null && !string.IsNullOrWhiteSpace(topic.Doc)) doc = topic.Doc; } _cache[key] = (found, resolvedTitle, doc); title = resolvedTitle; markdown = doc; return found; } private static readonly Dictionary _cache = new(); }