using System.Collections.ObjectModel; using System.Text; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using ClaudeDo.Ui.Helpers; using ClaudeDo.Ui.Services; namespace ClaudeDo.Ui.ViewModels.Islands; public sealed partial class PrepPanelViewModel : ViewModelBase, IDisposable { private readonly IWorkerClient _worker; private readonly StreamLineFormatter _formatter = new(); private readonly StringBuilder _prepClaudeBuf = new(); private readonly Action _onPrepStartedHandler; private readonly Action _onPrepLineHandler; private readonly Action _onPrepFinishedHandler; [ObservableProperty] private bool _isPrepRunning; public ObservableCollection PrepLog { get; } = new(); public bool ShowPrepEmptyState => !IsPrepRunning && PrepLog.Count == 0; partial void OnIsPrepRunningChanged(bool value) => OnPropertyChanged(nameof(ShowPrepEmptyState)); public PrepPanelViewModel(IWorkerClient worker) { _worker = worker; _onPrepStartedHandler = OnPrepStarted; _onPrepLineHandler = OnPrepLine; _onPrepFinishedHandler = OnPrepFinished; _worker.PrepStartedEvent += _onPrepStartedHandler; _worker.PrepLineEvent += _onPrepLineHandler; _worker.PrepFinishedEvent += _onPrepFinishedHandler; PrepLog.CollectionChanged += (_, _) => OnPropertyChanged(nameof(ShowPrepEmptyState)); } public void Dispose() { _worker.PrepStartedEvent -= _onPrepStartedHandler; _worker.PrepLineEvent -= _onPrepLineHandler; _worker.PrepFinishedEvent -= _onPrepFinishedHandler; } [RelayCommand] private async System.Threading.Tasks.Task PlanDayAsync() { try { await _worker.RunDailyPrepNowAsync(); } catch { } } public async System.Threading.Tasks.Task LoadLastPrepLogIfEmptyAsync() { if (IsPrepRunning || PrepLog.Count > 0) return; string text; try { text = await _worker.GetLastPrepLogAsync(); } catch { return; } if (IsPrepRunning || PrepLog.Count > 0) return; foreach (var line in text.Split('\n')) { var trimmed = line.TrimEnd('\r'); if (trimmed.Length > 0) AppendStdoutLine(trimmed); } } private void OnPrepStarted() { PrepLog.Clear(); IsPrepRunning = true; } private void OnPrepLine(string line) => AppendStdoutLine(line); private void OnPrepFinished(bool success) => IsPrepRunning = false; private void AppendStdoutLine(string line) { var formatted = _formatter.FormatLine(line); if (formatted is null) return; AppendClaudeText(formatted); } private void AppendClaudeText(string chunk) { _prepClaudeBuf.Append(chunk); while (true) { var text = _prepClaudeBuf.ToString(); var nl = text.IndexOf('\n'); if (nl < 0) break; var piece = text[..nl].TrimEnd('\r'); if (!string.IsNullOrWhiteSpace(piece)) PrepLog.Add(new LogLineViewModel { Kind = LogKind.Claude, Text = piece }); _prepClaudeBuf.Clear(); _prepClaudeBuf.Append(text[(nl + 1)..]); } } }