Forge
csharpdeeb25a2
1#nullable enable
2
3using Avalonia.Threading;
4using CascadeIDE.Cockpit.DataBus;
5using CascadeIDE.Features.Agent.Environment;
6using CascadeIDE.Features.Chat;
7using CascadeIDE.ViewModels;
8using CommunityToolkit.Mvvm.ComponentModel;
9using CommunityToolkit.Mvvm.Input;
10
11namespace CascadeIDE.ViewModels;
12
13/// <summary>AEE: PFD Verify Epoch instrument, chat trace, epoch stale on write (ADR 0148 W3–W5).</summary>
14public partial class MainWindowViewModel
15{
16 private readonly AgentVerifyEpochInstrument _verifyEpochInstrument = new();
17 private IDisposable? _agentEnvironmentBusSubscription;
18 private IDisposable? _verifyEpochDataBusBridge;
19 private IDisposable? _agentEnvironmentChatProjection;
20 private IDisposable? _agentEnvironmentChatProgressProjection;
21 private IDisposable? _agentEnvironmentWarmupBridge;
22 private IDisposable? _agentEnvironmentEpochStaleChatProjection;
23
24 public Features.Agent.Environment.IAgentEnvironmentService AgentEnvironment => _agentEnvironment;
25
26 public AgentVerifyEpochInstrument VerifyEpochInstrument => _verifyEpochInstrument;
27
28 [ObservableProperty]
29 [NotifyPropertyChangedFor(nameof(PfdBackgroundStatusText))]
30 [NotifyPropertyChangedFor(nameof(ShowPfdBackgroundStatusBar))]
31 [NotifyPropertyChangedFor(nameof(ShowPfdVerifyEpochExpandedPanel))]
32 private bool _isPfdVerifyEpochExpanded;
33
34 public bool ShowPfdVerifyEpochExpandedPanel =>
35 IsPfdVerifyEpochExpanded
36 && _settings.Agent.Environment.TimeAccounting.PfdInstrumentEnabled
37 && !string.IsNullOrWhiteSpace(PfdVerifyEpochExpandedText);
38
39 public string PfdVerifyEpochExpandedText => _verifyEpochInstrument.ExpandedText;
40
41 internal void EnsureAgentEnvironmentWiring()
42 {
43 if (_agentEnvironmentBusSubscription is not null)
44 return;
45
46 var accounting = _settings.Agent.Environment.TimeAccounting;
47
48 EnsurePfdBackgroundStatusSubscription();
49 EnsurePfdAgentEnvironmentTaskSubscription();
50
51 _verifyEpochInstrument.Changed += OnVerifyEpochInstrumentChanged;
52
53 _verifyEpochDataBusBridge = new AgentVerifyEpochDataBusBridge(_ideDataBus, _verifyEpochInstrument);
54
55 _agentEnvironmentBusSubscription = new AgentEnvironmentBusComposite(
56 _ideDataBus.Subscribe<AgentEnvironmentTaskChanged>(_ =>
57 UiScheduler.Default.Post(RefreshPfdBackgroundStatusBar, DispatcherPriority.Background)),
58 _ideDataBus.Subscribe<AgentRunCompleted>(_ =>
59 UiScheduler.Default.Post(RefreshPfdBackgroundStatusBar, DispatcherPriority.Background)),
60 _ideDataBus.Subscribe<AgentVerifyEpochStale>(_ =>
61 UiScheduler.Default.Post(OnAgentVerifyEpochStaleUi, DispatcherPriority.Background)));
62
63 if (accounting.ShowInChat)
64 {
65 _agentEnvironmentChatProjection = new AgentEnvironmentChatProjection(
66 _ideDataBus,
67 accounting,
68 AppendAgentEnvironmentChatTrace);
69
70 _agentEnvironmentEpochStaleChatProjection = new AgentEnvironmentEpochStaleChatProjection(
71 _ideDataBus,
72 accounting,
73 AppendAgentEnvironmentChatTrace);
74 }
75
76 _agentEnvironmentChatProgressProjection = new AgentEnvironmentChatProgressProjection(
77 _ideDataBus,
78 accounting,
79 AppendAgentEnvironmentChatTrace);
80
81 _agentEnvironmentWarmupBridge = new AgentEnvironmentWarmupBridge(_ideDataBus, _agentEnvironment);
82 }
83
84 private void OnVerifyEpochInstrumentChanged()
85 {
86 UiScheduler.Default.Post(() =>
87 {
88 OnPropertyChanged(nameof(PfdVerifyEpochExpandedText));
89 OnPropertyChanged(nameof(ShowPfdVerifyEpochExpandedPanel));
90 OnPropertyChanged(nameof(ShowPfdVerifyEpochRetry));
91 OnPropertyChanged(nameof(ShowPfdVerifyEpochExpandToggle));
92 RefreshPfdBackgroundStatusBar();
93 EnsureVerifyEpochActiveTicker();
94 }, DispatcherPriority.Background);
95 }
96
97 internal void NotifyAgentEnvironmentDocumentWrite(string? filePath)
98 {
99 _agentEnvironment.EpochTracker.NotifyWrite(filePath);
100 RefreshAgentVerifyEpochEditorDim(filePath);
101 MaybeScheduleAutoVerifyAfterCsWrite(filePath);
102 }
103
104 private void OnAgentVerifyEpochStaleUi()
105 {
106 RefreshPfdBackgroundStatusBar();
107 RefreshAgentVerifyEpochEditorDim(CurrentFilePath);
108 }
109
110 internal void RefreshAgentVerifyEpochEditorDim(string? filePath)
111 {
112 if (string.IsNullOrWhiteSpace(filePath))
113 return;
114
115 var isStale = _agentEnvironment.EpochTracker.IsPathUiStale(filePath);
116 RefreshActiveEditorEpochDimRequested?.Invoke(isStale);
117 }
118
119 internal void NotifyCideWindowFocusChanged(bool focused) =>
120 _agentEnvironment.NotifyCideWindowFocus(focused);
121
122 /// <summary>MainWindow: применить dim к активному редактору.</summary>
123 internal event Action<bool>? RefreshActiveEditorEpochDimRequested;
124
125 [RelayCommand]
126 private void TogglePfdVerifyEpochExpanded() =>
127 IsPfdVerifyEpochExpanded = !IsPfdVerifyEpochExpanded;
128
129 private void AppendAgentEnvironmentChatTrace(string text, bool green)
130 {
131 UiScheduler.Default.Post(() =>
132 {
133 ChatPanel.AppendAgentEnvironmentTrace(
134 text,
135 green ? ChatSlashCommandStatus.Succeeded : ChatSlashCommandStatus.Failed);
136 }, DispatcherPriority.Background);
137 }
138}
139
140file sealed class AgentEnvironmentBusComposite : IDisposable
141{
142 private readonly IDisposable[] _items;
143
144 public AgentEnvironmentBusComposite(params IDisposable[] items) => _items = items;
145
146 public void Dispose()
147 {
148 foreach (var d in _items)
149 d.Dispose();
150 }
151}
152
View only · write via MCP/CIDE