Forge
csharpdeeb25a2
1using System.Text.Json;
2using System.Text.Json.Serialization;
3using CascadeIDE.Services;
4
5namespace CascadeIDE.Features.Editor.Application.Monaco;
6
7/// <summary>cide-editor bridge (ADR 0162 §4).</summary>
8public static class CideEditorBridgeTypes
9{
10 public const string SetModel = "editor/setModel";
11 public const string ApplyEdits = "editor/applyEdits";
12 public const string SetDecorations = "editor/setDecorations";
13 public const string SetTheme = "editor/setTheme";
14 public const string SetStickyScroll = "editor/setStickyScroll";
15 public const string SetGutterGlyphs = "editor/setGutterGlyphs";
16 public const string SetIntelligence = "editor/setIntelligence";
17 public const string RevealRange = "editor/revealRange";
18 public const string SetSelectionByOffset = "editor/setSelectionByOffset";
19 public const string SetAgentReveal = "editor/setAgentReveal";
20 public const string ClearAgentReveal = "editor/clearAgentReveal";
21 public const string SetEpochDim = "editor/setEpochDim";
22
23 public const string DidChange = "editor/didChange";
24 public const string DidChangeCursorSelection = "editor/didChangeCursorSelection";
25 public const string DidScroll = "editor/didScroll";
26 public const string DidGutterClick = "editor/didGutterClick";
27 public const string Ready = "editor/ready";
28
29 /// <summary>Editor → host: tunnel hotkey from WebView2 (Ctrl+P etc.).</summary>
30 public const string HostShortcut = "host/shortcut";
31
32 /// <summary>Alt+drag selection released — drop target hit-test on host (Intercom composer).</summary>
33 public const string HostAttachDragComplete = "host/attach-drag-complete";
34
35 public const string RequestCompletion = "editor/requestCompletion";
36 public const string CompletionResult = "editor/completionResult";
37 public const string RequestHover = "editor/requestHover";
38 public const string HoverResult = "editor/hoverResult";
39 public const string RequestSignature = "editor/requestSignature";
40 public const string SignatureResult = "editor/signatureResult";
41}
42
43public sealed record CideEditorSetModelMessage(
44 string Uri,
45 string LanguageId,
46 string Text,
47 int Version);
48
49public sealed record CideEditorApplyEdit(
50 int StartOffset,
51 int Length,
52 string Text);
53
54public sealed record CideEditorApplyEditsMessage(
55 IReadOnlyList<CideEditorApplyEdit> Edits,
56 int ExpectedVersion);
57
58/// <summary>
59/// Decoration push DTO. Prefer <see cref="StartLine"/> for whole-line layers (diagnostics);
60/// use <see cref="StartOffset"/> for token spans (highlights). See monaco-presentation-projection-v1.md.
61/// </summary>
62public sealed record CideEditorDecoration(
63 int StartOffset,
64 int Length,
65 string ClassName,
66 string? HoverMessage,
67 bool IsWholeLine = false,
68 string? GlyphMarginClassName = null,
69 int? StartLine = null,
70 int? StartColumn = null,
71 int? EndLine = null,
72 int? EndColumn = null);
73
74public sealed record CideEditorSetSelectionMessage(int SelectionStart, int SelectionLength);
75
76public sealed record CideEditorSetAgentRevealMessage(
77 int StartLine,
78 int EndLine,
79 bool Persistent,
80 int? DurationMs);
81
82public sealed record CideEditorSetEpochDimMessage(bool Dimmed);
83
84public sealed record CideEditorSetDecorationsMessage(
85 string SetId,
86 IReadOnlyList<CideEditorDecoration> Decorations,
87 int? ExpectedModelVersion = null);
88
89public sealed record CideEditorStickyScrollMessage(string? Label);
90
91public sealed record CideEditorGutterGlyph(
92 int LineOneBased,
93 string TextGlyph,
94 string? ToolTip,
95 string VisualKind);
96
97public sealed record CideEditorSetGutterGlyphsMessage(
98 IReadOnlyList<CideEditorGutterGlyph> Glyphs);
99
100public sealed record CideEditorSetIntelligenceMessage(bool Enabled);
101
102public sealed record CideEditorRevealRangeMessage(
103 int StartLine,
104 int EndLine,
105 int? Column,
106 bool Select = true);
107
108public sealed record CideEditorDefinitionLocation(
109 string FilePath,
110 int Line,
111 int Column);
112
113public sealed record CideEditorReferenceLocation(
114 string FilePath,
115 int Line,
116 int Column,
117 int? EndLine = null,
118 int? EndColumn = null);
119
120public sealed record CideEditorReferencesResultMessage(
121 int RequestId,
122 IReadOnlyList<CideEditorReferenceLocation> Locations);
123
124public sealed record CideEditorFormatResultMessage(
125 int RequestId,
126 string? Text);
127
128public sealed record CideEditorCodeActionItem(
129 string Title,
130 string Kind,
131 string? Text,
132 int? ActionIndex = null);
133
134public sealed record CideEditorDocumentTextChange(
135 string FilePath,
136 string Text,
137 bool IsNewFile = false,
138 string? PreviousFilePath = null);
139
140public sealed record CideEditorWorkspaceEditResultMessage(
141 int RequestId,
142 bool Ok,
143 string? Error,
144 IReadOnlyList<CideEditorDocumentTextChange> Changes);
145
146public sealed record CideEditorCodeActionResultMessage(
147 int RequestId,
148 IReadOnlyList<CideEditorCodeActionItem> Actions);
149
150public sealed record CideEditorDefinitionResultMessage(
151 int RequestId,
152 CideEditorDefinitionLocation? Location);
153
154public sealed record CideEditorSetThemeMessage(string ThemeName);
155
156public sealed record CideEditorCompletionItem(
157 string Label,
158 string InsertText,
159 string? Detail,
160 string? Kind = null);
161
162public sealed record CideEditorCompletionResultMessage(
163 int RequestId,
164 IReadOnlyList<CideEditorCompletionItem> Items);
165
166public sealed record CideEditorHoverResultMessage(
167 int RequestId,
168 string? Markdown);
169
170public sealed record CideEditorSignatureResultMessage(
171 int RequestId,
172 string? Signature);
173
174public sealed record CideEditorInlayHint(
175 int Line,
176 int Column,
177 string Label,
178 string Kind = "type",
179 bool AtEndOfLine = false);
180
181public sealed record CideEditorInlayHintsResultMessage(
182 int RequestId,
183 IReadOnlyList<CideEditorInlayHint> Hints);
184
185public sealed record CideEditorCodeLensItem(
186 string Id,
187 int Line,
188 int Column,
189 string Title);
190
191public sealed record CideEditorCodeLensResultMessage(
192 int RequestId,
193 IReadOnlyList<CideEditorCodeLensItem> Lenses);
194
195public sealed record CideEditorSemanticTokensLegend(
196 IReadOnlyList<string> TokenTypes,
197 IReadOnlyList<string> TokenModifiers);
198
199public sealed record CideEditorSemanticTokensData(
200 IReadOnlyList<uint> Data,
201 string? ResultId);
202
203public sealed record CideEditorSemanticTokensResultMessage(
204 int RequestId,
205 IReadOnlyList<uint> Data,
206 string? ResultId);
207
208public sealed record CideEditorInboundMessage(
209 string Type,
210 [property: JsonPropertyName("version")] int? Version,
211 [property: JsonPropertyName("text")] string? Text,
212 [property: JsonPropertyName("caretOffset")] int? CaretOffset,
213 [property: JsonPropertyName("selectionStart")] int? SelectionStart,
214 [property: JsonPropertyName("selectionLength")] int? SelectionLength,
215 [property: JsonPropertyName("requestId")] int? RequestId,
216 [property: JsonPropertyName("line")] int? Line,
217 [property: JsonPropertyName("column")] int? Column,
218 [property: JsonPropertyName("topLine")] int? TopLine,
219 [property: JsonPropertyName("lensId")] string? LensId,
220 [property: JsonPropertyName("filePath")] string? FilePath,
221 [property: JsonPropertyName("error")] string? Error,
222 [property: JsonPropertyName("actionIndex")] int? ActionIndex,
223 [property: JsonPropertyName("newName")] string? NewName,
224 [property: JsonPropertyName("endLine")] int? EndLine,
225 [property: JsonPropertyName("endColumn")] int? EndColumn,
226 [property: JsonPropertyName("id")] string? ShortcutId);
227
228public static class CideEditorBridgeJson
229{
230 public static readonly JsonSerializerOptions Options = new()
231 {
232 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
233 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
234 };
235
236 public static string WrapOutbound(string type, object payload) =>
237 JsonSerializer.Serialize(new { type, payload }, Options);
238
239 public static CideEditorInboundMessage? TryParseInbound(string? body)
240 {
241 if (string.IsNullOrWhiteSpace(body))
242 return null;
243 try
244 {
245 using var doc = JsonDocument.Parse(body);
246 var root = doc.RootElement;
247 if (!root.TryGetProperty("type", out var typeEl))
248 return null;
249 var type = typeEl.GetString() ?? "";
250 int? version = TryInt(root, "version");
251 string? text = root.TryGetProperty("text", out var t) ? t.GetString() : null;
252 int? caret = TryInt(root, "caretOffset");
253 int? selStart = TryInt(root, "selectionStart");
254 int? selLen = TryInt(root, "selectionLength");
255 int? requestId = TryInt(root, "requestId");
256 int? line = TryInt(root, "line");
257 int? column = TryInt(root, "column");
258 int? topLine = TryInt(root, "topLine");
259 string? lensId = root.TryGetProperty("lensId", out var lens) ? lens.GetString() : null;
260 string? filePath = root.TryGetProperty("filePath", out var fp) ? fp.GetString() : null;
261 string? error = root.TryGetProperty("error", out var err) ? err.GetString() : null;
262 int? actionIndex = TryInt(root, "actionIndex");
263 string? newName = root.TryGetProperty("newName", out var nn) ? nn.GetString() : null;
264 int? endLine = TryInt(root, "endLine");
265 int? endColumn = TryInt(root, "endColumn");
266 string? shortcutId = root.TryGetProperty("id", out var idEl) ? idEl.GetString() : null;
267 return new CideEditorInboundMessage(
268 type, version, text, caret, selStart, selLen, requestId, line, column, topLine, lensId, filePath, error,
269 actionIndex, newName, endLine, endColumn, shortcutId);
270 }
271 catch
272 {
273 return null;
274 }
275 }
276
277 private static int? TryInt(JsonElement root, string name) =>
278 root.TryGetProperty(name, out var el) && el.TryGetInt32(out var value) ? value : null;
279}
280
281public static class CideEditorLanguageIds
282{
283 public static string FromFilePath(string? filePath) =>
284 EditorLanguageSupport.GetMonacoLanguageId(filePath);
285
286 public static bool SupportsRoslynIntelligence(string? filePath) =>
287 string.Equals(Path.GetExtension(filePath), ".cs", StringComparison.OrdinalIgnoreCase)
288 || string.Equals(Path.GetExtension(filePath), ".csx", StringComparison.OrdinalIgnoreCase);
289}
290
291public static class MonacoEditorAssetLocator
292{
293 public const string VirtualHostName = "cide-editor.local";
294
295 public static string GetCideEditorRoot()
296 {
297 var baseDir = AppContext.BaseDirectory;
298 var candidate = Path.Combine(baseDir, "Assets", "cide-editor");
299 if (Directory.Exists(candidate))
300 return candidate;
301
302 var dev = Path.GetFullPath(Path.Combine(baseDir, "..", "..", "..", "Assets", "cide-editor"));
303 if (Directory.Exists(dev))
304 return dev;
305
306 return candidate;
307 }
308
309 public static string GetIndexHtmlPath()
310 {
311 var path = Path.Combine(GetCideEditorRoot(), "index.html");
312 return File.Exists(path) ? path : path;
313 }
314
315 public static string GetMonacoVsPath()
316 {
317 var local = Path.Combine(GetCideEditorRoot(), "monaco", "min", "vs");
318 return Directory.Exists(local) ? local.Replace('\\', '/') : "";
319 }
320
321 public static Uri GetIndexUri() =>
322 new($"https://{VirtualHostName}/index.html");
323
324 public static Uri GetFileIndexUri()
325 {
326 var path = Path.GetFullPath(GetIndexHtmlPath());
327 return new Uri(path);
328 }
329}
330
331
View only · write via MCP/CIDE