| 1 | using CascadeIDE.Cockpit; |
| 2 | using CascadeIDE.Cockpit.Cds; |
| 3 | using CascadeIDE.Cockpit.Composition.HostSurface; |
| 4 | using CascadeIDE.Cockpit.Composition.Shell; |
| 5 | using CascadeIDE.Contracts; |
| 6 | using CascadeIDE.Features.Agent.Environment; |
| 7 | using CascadeIDE.Features.UiChrome; |
| 8 | using CascadeIDE.Lang; |
| 9 | using CascadeIDE.Models; |
| 10 | |
| 11 | namespace CascadeIDE.Features.Shell.Application; |
| 12 | |
| 13 | /// <summary> |
| 14 | /// Статические проекции для вычисляемых свойств главного окна (видимость, подписи, mount-контекст без логики на VM). |
| 15 | /// </summary> |
| 16 | [PresentationProjection] |
| 17 | public static class MainWindowPresentationSurfaceProjection |
| 18 | { |
| 19 | /// <summary>Кадр хоста: intent + CDS style → shell + инструменты (ADR 0036 п.3, 0047).</summary> |
| 20 | public static MainWindowHostSurfaceFrame ComposeHostSurfaceFrame( |
| 21 | IMainWindowHostSurfaceInput host, |
| 22 | string normalizedUiMode, |
| 23 | MfdShellPage currentMfdShellPage, |
| 24 | PrimaryWorkSurfaceKind primaryWorkSurface) => |
| 25 | MainWindowHostSurfaceProjection.ComposeFrame( |
| 26 | host, |
| 27 | ResolveExpandedMfdWidthPixels(normalizedUiMode, currentMfdShellPage, primaryWorkSurface), |
| 28 | UiWorkspaceLayoutRuntimeMetrics.MfdRegionCollapsedWidthPixels); |
| 29 | |
| 30 | /// <summary> |
| 31 | /// Ширина колонки MFD: для страницы «Чат» — не уже <see cref="UiWorkspaceLayoutRuntimeMetrics.MfdRegionExpandedAgentChatWidthPixels"/> (520). |
| 32 | /// </summary> |
| 33 | public static int ResolveExpandedMfdWidthPixels( |
| 34 | string normalizedUiMode, |
| 35 | MfdShellPage currentMfdShellPage, |
| 36 | PrimaryWorkSurfaceKind primaryWorkSurface) |
| 37 | { |
| 38 | _ = primaryWorkSurface; |
| 39 | var modeDefault = UiModeCatalog.GetMfdRegionExpandedWidthPixels(normalizedUiMode); |
| 40 | if (currentMfdShellPage != MfdShellPage.Chat) |
| 41 | return modeDefault; |
| 42 | |
| 43 | return Math.Max(modeDefault, UiWorkspaceLayoutRuntimeMetrics.MfdRegionExpandedAgentChatWidthPixels); |
| 44 | } |
| 45 | |
| 46 | public const string DefaultRiskSummaryPlaceholder = "Риски не зафиксированы."; |
| 47 | public const string DefaultResultSummaryPlaceholder = "Результатов пока нет."; |
| 48 | |
| 49 | public static string ResolveWindowTitle(string normalizedUiMode) |
| 50 | { |
| 51 | var o = UiModeCatalog.GetWindowTitleOverride(normalizedUiMode); |
| 52 | if (o is not null) |
| 53 | return o; |
| 54 | return UiModeFamilyResolver.FromNormalizedMode(normalizedUiMode) switch |
| 55 | { |
| 56 | UiModeFamily.Power => "CascadeIDE — Power Mode [Autonomous Agent Cockpit]", |
| 57 | UiModeFamily.AgentChat => "CascadeIDE — Agent Chat", |
| 58 | UiModeFamily.Debug => "CascadeIDE — Debug", |
| 59 | UiModeFamily.Editor => "CascadeIDE — Editor", |
| 60 | _ => "CascadeIDE", |
| 61 | }; |
| 62 | } |
| 63 | |
| 64 | public static string InstrumentMountDisplayStyle(DisplaySettings display) => |
| 65 | string.IsNullOrWhiteSpace(display.Mount.DefaultStyle) |
| 66 | ? InstrumentMountPolicyIds.V1 |
| 67 | : display.Mount.DefaultStyle.Trim(); |
| 68 | |
| 69 | public static string MountPolicySurfaceId(AttentionLayoutSurfaceKind surface) => |
| 70 | surface switch |
| 71 | { |
| 72 | AttentionLayoutSurfaceKind.MainWindowDockedGrid => "main_window_docked_grid", |
| 73 | AttentionLayoutSurfaceKind.MainWindowPlusMfdHostTopLevel => "main_window_plus_mfd_host_top_level", |
| 74 | AttentionLayoutSurfaceKind.MainWindowPlusPfdHostTopLevel => "main_window_plus_pfd_host_top_level", |
| 75 | AttentionLayoutSurfaceKind.MainWindowPlusPfdMfdHostTopLevel => "main_window_plus_pfd_mfd_host_top_level", |
| 76 | _ => "main_window_docked_grid", |
| 77 | }; |
| 78 | |
| 79 | /// <summary>Сплиттер и колонка main grid: ширина слота > 0.</summary> |
| 80 | public static bool IsMainGridSplitColumnVisible(int columnWidthPixels) => columnWidthPixels > 0; |
| 81 | |
| 82 | public static bool IsIdeHealthSkiaMountVisibleInDockedColumn(bool useSkiaInstrumentMount, bool columnVisible) => |
| 83 | useSkiaInstrumentMount && columnVisible; |
| 84 | |
| 85 | public static bool IsIdeHealthSkiaMountVisibleForHostWindow(bool useSkiaInstrumentMount, bool hostShellOpen) => |
| 86 | useSkiaInstrumentMount && hostShellOpen; |
| 87 | |
| 88 | public static string ResolveInstrumentMountStyleForSlot( |
| 89 | IInstrumentMountPolicyResolver resolver, |
| 90 | DisplaySettings displaySettings, |
| 91 | AttentionLayoutSurfaceKind attentionSurface, |
| 92 | string slotId, |
| 93 | string instrumentId) |
| 94 | { |
| 95 | var surfaceId = MountPolicySurfaceId(attentionSurface); |
| 96 | return resolver.Resolve(displaySettings, surfaceId, slotId, instrumentId); |
| 97 | } |
| 98 | |
| 99 | public static bool IsMfdContourContentVisible( |
| 100 | bool problemsPanelVisible, |
| 101 | bool isTerminalVisible, |
| 102 | bool isBuildOutputVisible, |
| 103 | bool instrumentationTabs, |
| 104 | bool isGitPanelVisible) => |
| 105 | problemsPanelVisible || isTerminalVisible || isBuildOutputVisible || instrumentationTabs || isGitPanelVisible; |
| 106 | |
| 107 | public static string TelemetryButtonCaption(bool terminalVisible) => |
| 108 | terminalVisible ? "Telemetry: on" : "Show telemetry"; |
| 109 | |
| 110 | public static string MfdRegionToggleCaption(bool isMfdRegionExpanded) => |
| 111 | isMfdRegionExpanded ? "◀" : "▶"; |
| 112 | |
| 113 | public static string SafetyLevelDescription(string safetyLevel) => |
| 114 | safetyLevel switch |
| 115 | { |
| 116 | AgentSafetyLevel.Observe => Resources.Safety_Description_Observe, |
| 117 | AgentSafetyLevel.Confirm => Resources.Safety_Description_Confirm, |
| 118 | AgentSafetyLevel.Autonomous => Resources.Safety_Description_Autonomous, |
| 119 | _ => "", |
| 120 | }; |
| 121 | |
| 122 | public static double SafetyBadgeOpacity(bool isActiveLevel) => isActiveLevel ? 1 : 0.38; |
| 123 | |
| 124 | /// <summary>Текст задан и отличается от плейсхолдера «нет данных».</summary> |
| 125 | public static bool IsAgentSummaryVisibleComparedToPlaceholder(string? text, string placeholder) => |
| 126 | !string.IsNullOrWhiteSpace(text) |
| 127 | && !string.Equals(text, placeholder, StringComparison.Ordinal); |
| 128 | |
| 129 | /// <remarks>Повторяет ветвление прежних геттеров <c>PfdIdeHealthMountContext</c>/<c>MfdIdeHealthMountContext</c>.</remarks> |
| 130 | public static IdeHealthStatusMountContext? ResolvePfdIdeHealthMountContext( |
| 131 | bool useSkiaInstrumentMount, |
| 132 | bool isPfdHostWindowShellOpen, |
| 133 | bool isPfdColumnVisible, |
| 134 | IInstrumentMountPolicyResolver resolver, |
| 135 | DisplaySettings displaySettings, |
| 136 | string mountPolicySurfaceIdForMainDockedGrid, |
| 137 | IdeHealthStatusMountPayload payload) |
| 138 | { |
| 139 | if (!useSkiaInstrumentMount) |
| 140 | return null; |
| 141 | if (isPfdHostWindowShellOpen) |
| 142 | return IdeHealthMountContextFactory.Create( |
| 143 | resolver, |
| 144 | displaySettings, |
| 145 | "main_window_plus_pfd_host_top_level", |
| 146 | CockpitSlotIds.Pfd, |
| 147 | payload); |
| 148 | if (isPfdColumnVisible) |
| 149 | return IdeHealthMountContextFactory.Create( |
| 150 | resolver, |
| 151 | displaySettings, |
| 152 | mountPolicySurfaceIdForMainDockedGrid, |
| 153 | CockpitSlotIds.Pfd, |
| 154 | payload); |
| 155 | return null; |
| 156 | } |
| 157 | |
| 158 | public static IdeHealthStatusMountContext? ResolveMfdIdeHealthMountContext( |
| 159 | bool useSkiaInstrumentMount, |
| 160 | bool isMfdHostWindowShellOpen, |
| 161 | bool isMfdColumnVisible, |
| 162 | IInstrumentMountPolicyResolver resolver, |
| 163 | DisplaySettings displaySettings, |
| 164 | string mountPolicySurfaceIdForMainDockedGrid, |
| 165 | IdeHealthStatusMountPayload payload) |
| 166 | { |
| 167 | if (!useSkiaInstrumentMount) |
| 168 | return null; |
| 169 | if (isMfdHostWindowShellOpen) |
| 170 | return IdeHealthMountContextFactory.Create( |
| 171 | resolver, |
| 172 | displaySettings, |
| 173 | "main_window_plus_mfd_host_top_level", |
| 174 | CockpitSlotIds.Mfd, |
| 175 | payload); |
| 176 | if (isMfdColumnVisible) |
| 177 | return IdeHealthMountContextFactory.Create( |
| 178 | resolver, |
| 179 | displaySettings, |
| 180 | mountPolicySurfaceIdForMainDockedGrid, |
| 181 | CockpitSlotIds.Mfd, |
| 182 | payload); |
| 183 | return null; |
| 184 | } |
| 185 | } |
| 186 | |