Forge
csharpdeeb25a2
1using Avalonia.Threading;
2using CascadeIDE.Features.IdeMcp.Application;
3using CascadeIDE.Features.UiChrome.Application;
4using CascadeIDE.Features.Workspace.Application;
5using CascadeIDE.Features.UiChrome;
6using CascadeIDE.Models;
7
8namespace CascadeIDE.ViewModels;
9
10/// <summary>Git + workspace UI.</summary>
11public partial class MainWindowViewModel
12{
13 internal static string NormalizeUiMode(string? mode) => UiChromeViewModel.NormalizeUiMode(mode);
14
15 private Task RefreshGitSummaryAsync() => Chrome.RefreshGitSummaryAsync(RunGitCommandAsync);
16
17 /// <summary>Git для полоски Workspace Health (<see cref="RefreshGitSummaryAsync"/>); MCP git — <see cref="IdeMcpGitWorkspaceSession"/>.</summary>
18 private async Task<(bool Success, int ExitCode, string Output)> RunGitCommandAsync(IReadOnlyList<string> args)
19 {
20 var workspace = GetWorkspacePath();
21 if (string.IsNullOrWhiteSpace(workspace) || !Directory.Exists(workspace))
22 return (false, -1, IdeMcpGitOrchestrator.WorkspaceUnavailableMessage());
23 return await _gitRunner.RunAsync(args, workspace).ConfigureAwait(false);
24 }
25
26 private void InitializeAgentUiDefaults()
27 {
28 // Keep operation/trace feeds empty until real runtime events arrive.
29 // This avoids demo-like placeholder content in production UI.
30 }
31
32 private void RegisterAgentFeedHandlers()
33 {
34 FocusPlanItems.CollectionChanged += (_, _) => OnPropertyChanged(nameof(HasFocusPlanItems));
35 }
36
37 private void ApplyUiModeLayout(string mode, bool persist)
38 {
39 var plan = UiModeLayoutApplyProjection.Create(mode, persist);
40 var spec = plan.Spec;
41
42 IsPfdRegionExpanded = spec.PfdRegionExpanded;
43 IsBuildOutputVisible = spec.BuildOutputVisible;
44 IsTerminalVisible = spec.TerminalVisible;
45 IsMfdRegionExpanded = spec.MfdRegionExpanded;
46 EditorGroupCount = spec.EditorGroupCount;
47 IsInstrumentationDockVisible = spec.InstrumentationDockVisible;
48
49 CoerceMfdShellPageToAllowed();
50 CurrentMfdShellPage = UiModeLayoutApplyProjection.ResolveMfdPageAfterApply(plan, CurrentMfdShellPage);
51
52 _ = spec.ThemeSlot switch
53 {
54 UiModeThemeSlot.PowerCockpit => Services.UiThemeApply.ApplyOnUiThreadAsync(Services.UiThemeApply.GetPowerCockpitConceptThemeJson()),
55 UiModeThemeSlot.Dark => Services.UiThemeApply.ApplyOnUiThreadAsync(Services.UiThemeApply.GetDarkThemeJson()),
56 UiModeThemeSlot.CursorLike => Services.UiThemeApply.ApplyOnUiThreadAsync(Services.UiThemeApply.GetCursorLikeThemeJson()),
57 _ => Services.UiThemeApply.ApplyOnUiThreadAsync(Services.UiThemeApply.GetCursorLikeThemeJson())
58 };
59
60 UiModeLayoutApplyProjection.PersistWorkspaceMode(
61 plan,
62 IsPfdRegionExpanded,
63 IsTerminalVisible,
64 _settings,
65 SaveSettingsIfChanged);
66 }
67
68 internal string GetWorkspacePath() => WorkspaceDirectoryFromSolutionPath.Resolve(Workspace.SolutionPath);
69
70 partial void OnSelectedOllamaModelChanged(string? value)
71 {
72 ChatPanel.RefreshSendChatCommandState();
73 if (value == InstallNewSentinel)
74 {
75 SelectedModelDetails = "";
76 return;
77 }
78 if (!string.IsNullOrEmpty(value))
79 {
80 LastSelectedRealModel = value;
81 _settings.Ai.Local.Ollama.Model = value;
82 SaveSettingsIfChanged();
83 _ = LoadModelDetailsAsync(value);
84 }
85 else
86 SelectedModelDetails = "";
87 }
88
89 private async Task LoadModelDetailsAsync(string modelName)
90 {
91 try
92 {
93 var details = await _ollama.GetModelDetailsAsync(modelName).ConfigureAwait(false);
94 await UiScheduler.Default.InvokeAsync(() =>
95 {
96 if (SelectedOllamaModel == modelName)
97 SelectedModelDetails = details?.ToShortString() ?? "";
98 });
99 }
100 catch
101 {
102 await UiScheduler.Default.InvokeAsync(() =>
103 {
104 if (SelectedOllamaModel == modelName)
105 SelectedModelDetails = "";
106 });
107 }
108 }
109
110 public async Task RefreshOllamaAsync()
111 {
112 OllamaStatus = "Проверка Ollama…";
113 OllamaAvailable = await _ollama.IsAvailableAsync();
114 if (OllamaAvailable)
115 {
116 var names = await _ollama.GetModelNamesAsync();
117 OllamaModels.Clear();
118 OllamaModelChoices.Clear();
119 foreach (var n in names)
120 {
121 OllamaModels.Add(n);
122 OllamaModelChoices.Add(n);
123 }
124 OllamaModelChoices.Add(InstallNewSentinel);
125 var preferred = _settings.Ai.Local.Ollama.Model?.Trim();
126 SelectedOllamaModel = !string.IsNullOrEmpty(preferred) && OllamaModels.Contains(preferred)
127 ? preferred
128 : OllamaModels.FirstOrDefault();
129 if (LastSelectedRealModel is null && OllamaModels.Count > 0)
130 LastSelectedRealModel = OllamaModels[0];
131 OllamaStatus = names.Count > 0
132 ? $"Ollama: {names.Count} моделей"
133 : "Ollama запущен, моделей нет (ollama pull <model>)";
134 }
135 else
136 {
137 OllamaModels.Clear();
138 OllamaStatus = "Ollama недоступен (localhost:11434). Установи и запусти Ollama.";
139 }
140 }
141}
142
View only · write via MCP/CIDE