Forge
csharpdeeb25a2
1using System.Collections.Immutable;
2
3namespace CascadeIDE.Features.UiChrome;
4
5/// <summary>
6/// Карта «поверхность → зона внимания», загружаемая из <c>UiModes/workspace.toml</c> (<c>[routing.attention]</c>, ADR 0021/0051).
7/// Дефолты совпадают с семантикой ADR; TOML накладывает переопределения.
8/// </summary>
9public static class AttentionZonePanelRuntime
10{
11 private static readonly IReadOnlyDictionary<string, string> IntentToPanelId = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
12 {
13 [AttentionRoutingIntentIds.SolutionExplorer] = AttentionPanelIds.SolutionExplorer,
14 [AttentionRoutingIntentIds.Chat] = AttentionPanelIds.ChatPanel,
15 [AttentionRoutingIntentIds.Git] = AttentionPanelIds.Git,
16 [AttentionRoutingIntentIds.Terminal] = AttentionPanelIds.Terminal,
17 [AttentionRoutingIntentIds.Editor] = AttentionPanelIds.Editor,
18 };
19
20 private static ImmutableDictionary<string, AttentionZone> _byPanel = BuildDefaultMap();
21
22 private static ImmutableDictionary<string, AttentionZone> BuildDefaultMap()
23 {
24 var b = ImmutableDictionary.CreateBuilder<string, AttentionZone>(StringComparer.OrdinalIgnoreCase);
25 b.Add(AttentionPanelIds.SolutionExplorer, AttentionZone.Pfd);
26 b.Add(AttentionPanelIds.ChatPanel, AttentionZone.Mfd);
27 b.Add(AttentionPanelIds.Git, AttentionZone.Mfd);
28 b.Add(AttentionPanelIds.Terminal, AttentionZone.Mfd);
29 b.Add(AttentionPanelIds.Editor, AttentionZone.Forward);
30 b.Add(AttentionPanelIds.EditorHud, AttentionZone.Hud);
31 return b.ToImmutable();
32 }
33
34 /// <summary>Сброс к дефолтам из кода (тесты, повторная загрузка каталога).</summary>
35 internal static void ResetToCodeDefaults() =>
36 _byPanel = BuildDefaultMap();
37
38 /// <summary>Применяет метрики workspace и карту панелей из распарсенного TOML.</summary>
39 internal static void ApplyWorkspaceToml(Features.Workspace.RepositoryWorkspaceToml? w)
40 {
41 var map = BuildDefaultMap();
42 if (w?.Routing?.Attention is { Count: > 0 } overrides)
43 {
44 var b = map.ToBuilder();
45 foreach (var kv in overrides)
46 {
47 var intent = kv.Key.Trim();
48 if (intent.Length == 0)
49 continue;
50 if (!IntentToPanelId.TryGetValue(intent, out var panelId))
51 {
52 global::System.Diagnostics.Debug.WriteLine($"AttentionZonePanelRuntime: unknown attention intent — {intent}");
53 continue;
54 }
55 var raw = kv.Value?.Trim();
56 if (string.IsNullOrEmpty(raw))
57 continue;
58 if (AttentionZoneExtensions.TryParseCanonicalId(raw, out var zone))
59 {
60 if (IsIntentZoneAllowed(intent, zone))
61 b[panelId] = zone;
62 else
63 global::System.Diagnostics.Debug.WriteLine(
64 $"AttentionZonePanelRuntime: zone '{raw}' is not allowed for intent '{intent}'");
65 }
66 else
67 global::System.Diagnostics.Debug.WriteLine($"AttentionZonePanelRuntime: unknown zone id — {raw} (intent {intent})");
68 }
69
70 map = b.ToImmutable();
71 }
72
73 _byPanel = map;
74 }
75
76 /// <summary>Зона для известной поверхности; <see langword="false"/>, если id не задан в карте.</summary>
77 public static bool TryGetZone(string panelId, out AttentionZone zone) =>
78 _byPanel.TryGetValue(panelId, out zone);
79
80 /// <summary>Текущая карта (после загрузки <c>workspace.toml</c>).</summary>
81 public static IReadOnlyDictionary<string, AttentionZone> CurrentMap => _byPanel;
82
83 private static bool IsIntentZoneAllowed(string intent, AttentionZone zone)
84 {
85 if (string.Equals(intent, AttentionRoutingIntentIds.Editor, StringComparison.OrdinalIgnoreCase))
86 return zone == AttentionZone.Forward;
87
88 return zone.IsSpatialAnchor();
89 }
90}
91
View only · write via MCP/CIDE