blob: fd42e603c7ac9362c16638f07932251ad779e5e0 [file] [log] [blame]
Philip Zeyliger272a90e2025-05-16 14:49:51 -07001import { css, html, LitElement } from "lit";
2import { customElement, property, state } from "lit/decorators.js";
3import "./sketch-monaco-view";
4import "./sketch-diff-range-picker";
5import "./sketch-diff-file-picker";
6import "./sketch-diff-empty-view";
Autoformatter8c463622025-05-16 21:54:17 +00007import {
8 GitDiffFile,
9 GitDataService,
10 DefaultGitDataService,
11} from "./git-data-service";
Philip Zeyliger272a90e2025-05-16 14:49:51 -070012import { DiffRange } from "./sketch-diff-range-picker";
13
14/**
15 * A component that displays diffs using Monaco editor with range and file pickers
16 */
17@customElement("sketch-diff2-view")
18export class SketchDiff2View extends LitElement {
19 /**
20 * Handles comment events from the Monaco editor and forwards them to the chat input
21 * using the same event format as the original diff view for consistency.
22 */
23 private handleMonacoComment(event: CustomEvent) {
24 try {
25 // Validate incoming data
26 if (!event.detail || !event.detail.formattedComment) {
Autoformatter8c463622025-05-16 21:54:17 +000027 console.error("Invalid comment data received");
Philip Zeyliger272a90e2025-05-16 14:49:51 -070028 return;
29 }
Autoformatter8c463622025-05-16 21:54:17 +000030
Philip Zeyliger272a90e2025-05-16 14:49:51 -070031 // Create and dispatch event using the standardized format
Autoformatter8c463622025-05-16 21:54:17 +000032 const commentEvent = new CustomEvent("diff-comment", {
Philip Zeyliger272a90e2025-05-16 14:49:51 -070033 detail: { comment: event.detail.formattedComment },
34 bubbles: true,
Autoformatter8c463622025-05-16 21:54:17 +000035 composed: true,
Philip Zeyliger272a90e2025-05-16 14:49:51 -070036 });
Autoformatter8c463622025-05-16 21:54:17 +000037
Philip Zeyliger272a90e2025-05-16 14:49:51 -070038 this.dispatchEvent(commentEvent);
39 } catch (error) {
Autoformatter8c463622025-05-16 21:54:17 +000040 console.error("Error handling Monaco comment:", error);
Philip Zeyliger272a90e2025-05-16 14:49:51 -070041 }
42 }
Autoformatter8c463622025-05-16 21:54:17 +000043
Philip Zeyliger272a90e2025-05-16 14:49:51 -070044 /**
45 * Handle save events from the Monaco editor
46 */
47 private async handleMonacoSave(event: CustomEvent) {
48 try {
49 // Validate incoming data
Autoformatter8c463622025-05-16 21:54:17 +000050 if (
51 !event.detail ||
52 !event.detail.path ||
53 event.detail.content === undefined
54 ) {
55 console.error("Invalid save data received");
Philip Zeyliger272a90e2025-05-16 14:49:51 -070056 return;
57 }
Autoformatter8c463622025-05-16 21:54:17 +000058
Philip Zeyliger272a90e2025-05-16 14:49:51 -070059 const { path, content } = event.detail;
Autoformatter8c463622025-05-16 21:54:17 +000060
Philip Zeyliger272a90e2025-05-16 14:49:51 -070061 // Get Monaco view component
Autoformatter8c463622025-05-16 21:54:17 +000062 const monacoView = this.shadowRoot?.querySelector("sketch-monaco-view");
Philip Zeyliger272a90e2025-05-16 14:49:51 -070063 if (!monacoView) {
Autoformatter8c463622025-05-16 21:54:17 +000064 console.error("Monaco view not found");
Philip Zeyliger272a90e2025-05-16 14:49:51 -070065 return;
66 }
Autoformatter8c463622025-05-16 21:54:17 +000067
Philip Zeyliger272a90e2025-05-16 14:49:51 -070068 try {
69 await this.gitService?.saveFileContent(path, content);
70 console.log(`File saved: ${path}`);
71 (monacoView as any).notifySaveComplete(true);
72 } catch (error) {
Autoformatter8c463622025-05-16 21:54:17 +000073 console.error(
74 `Error saving file: ${error instanceof Error ? error.message : String(error)}`,
75 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -070076 (monacoView as any).notifySaveComplete(false);
77 }
78 } catch (error) {
Autoformatter8c463622025-05-16 21:54:17 +000079 console.error("Error handling save:", error);
Philip Zeyliger272a90e2025-05-16 14:49:51 -070080 }
81 }
82 @property({ type: String })
83 initialCommit: string = "";
Autoformatter8c463622025-05-16 21:54:17 +000084
Philip Zeyliger272a90e2025-05-16 14:49:51 -070085 // The commit to show - used when showing a specific commit from timeline
86 @property({ type: String })
87 commit: string = "";
88
89 @property({ type: String })
90 selectedFilePath: string = "";
91
92 @state()
93 private files: GitDiffFile[] = [];
Autoformatter8c463622025-05-16 21:54:17 +000094
Philip Zeyliger272a90e2025-05-16 14:49:51 -070095 @state()
Autoformatter8c463622025-05-16 21:54:17 +000096 private currentRange: DiffRange = { type: "range", from: "", to: "HEAD" };
Philip Zeyliger272a90e2025-05-16 14:49:51 -070097
98 @state()
99 private originalCode: string = "";
100
101 @state()
102 private modifiedCode: string = "";
Autoformatter8c463622025-05-16 21:54:17 +0000103
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700104 @state()
105 private isRightEditable: boolean = false;
106
107 @state()
108 private loading: boolean = false;
109
110 @state()
111 private error: string | null = null;
112
113 static styles = css`
114 :host {
115 display: flex;
116 height: 100%;
117 flex: 1;
118 flex-direction: column;
119 min-height: 0; /* Critical for flex child behavior */
120 overflow: hidden;
121 position: relative; /* Establish positioning context */
122 }
123
124 .controls {
125 padding: 8px 16px;
126 border-bottom: 1px solid var(--border-color, #e0e0e0);
127 background-color: var(--background-light, #f8f8f8);
128 flex-shrink: 0; /* Prevent controls from shrinking */
129 }
Autoformatter8c463622025-05-16 21:54:17 +0000130
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700131 .controls-container {
132 display: flex;
133 flex-direction: column;
134 gap: 12px;
135 }
Autoformatter8c463622025-05-16 21:54:17 +0000136
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700137 .range-row {
138 width: 100%;
139 display: flex;
140 }
Autoformatter8c463622025-05-16 21:54:17 +0000141
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700142 .file-row {
143 width: 100%;
144 display: flex;
145 justify-content: space-between;
146 align-items: center;
147 gap: 10px;
148 }
Autoformatter8c463622025-05-16 21:54:17 +0000149
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700150 sketch-diff-range-picker {
151 width: 100%;
152 }
Autoformatter8c463622025-05-16 21:54:17 +0000153
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700154 sketch-diff-file-picker {
155 flex: 1;
156 }
Autoformatter8c463622025-05-16 21:54:17 +0000157
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700158 .view-toggle-button {
159 background-color: #f0f0f0;
160 border: 1px solid #ccc;
161 border-radius: 4px;
162 padding: 6px 12px;
163 font-size: 12px;
164 cursor: pointer;
165 white-space: nowrap;
166 transition: background-color 0.2s;
167 }
Autoformatter8c463622025-05-16 21:54:17 +0000168
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700169 .view-toggle-button:hover {
170 background-color: #e0e0e0;
171 }
172
173 .diff-container {
174 flex: 1;
175 overflow: hidden;
176 display: flex;
177 flex-direction: column;
178 min-height: 0; /* Critical for flex child to respect parent height */
179 position: relative; /* Establish positioning context */
180 height: 100%; /* Take full height */
181 }
182
183 .diff-content {
184 flex: 1;
185 overflow: hidden;
186 min-height: 0; /* Required for proper flex behavior */
187 display: flex; /* Required for child to take full height */
188 position: relative; /* Establish positioning context */
189 height: 100%; /* Take full height */
190 }
191
Autoformatter8c463622025-05-16 21:54:17 +0000192 .loading,
193 .empty-diff {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700194 display: flex;
195 align-items: center;
196 justify-content: center;
197 height: 100%;
198 font-family: var(--font-family, system-ui, sans-serif);
199 }
Autoformatter8c463622025-05-16 21:54:17 +0000200
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700201 .empty-diff {
202 color: var(--text-secondary-color, #666);
203 font-size: 16px;
204 text-align: center;
205 }
206
207 .error {
208 color: var(--error-color, #dc3545);
209 padding: 16px;
210 font-family: var(--font-family, system-ui, sans-serif);
211 }
212
213 sketch-monaco-view {
214 --editor-width: 100%;
215 --editor-height: 100%;
216 flex: 1; /* Make Monaco view take full height */
217 display: flex; /* Required for child to take full height */
218 position: absolute; /* Absolute positioning to take full space */
219 top: 0;
220 left: 0;
221 right: 0;
222 bottom: 0;
223 height: 100%; /* Take full height */
Autoformatter8c463622025-05-16 21:54:17 +0000224 width: 100%; /* Take full width */
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700225 }
226 `;
227
228 @property({ attribute: false, type: Object })
229 gitService!: GitDataService;
Autoformatter8c463622025-05-16 21:54:17 +0000230
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700231 // The gitService must be passed from parent to ensure proper dependency injection
232
233 constructor() {
234 super();
Autoformatter8c463622025-05-16 21:54:17 +0000235 console.log("SketchDiff2View initialized");
236
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700237 // Fix for monaco-aria-container positioning
238 // Add a global style to ensure proper positioning of aria containers
Autoformatter8c463622025-05-16 21:54:17 +0000239 const styleElement = document.createElement("style");
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700240 styleElement.textContent = `
241 .monaco-aria-container {
242 position: absolute !important;
243 top: 0 !important;
244 left: 0 !important;
245 width: 1px !important;
246 height: 1px !important;
247 overflow: hidden !important;
248 clip: rect(1px, 1px, 1px, 1px) !important;
249 white-space: nowrap !important;
250 margin: 0 !important;
251 padding: 0 !important;
252 border: 0 !important;
253 z-index: -1 !important;
254 }
255 `;
256 document.head.appendChild(styleElement);
257 }
258
259 connectedCallback() {
260 super.connectedCallback();
261 // Initialize with default range and load data
262 // Get base commit if not set
Autoformatter8c463622025-05-16 21:54:17 +0000263 if (
264 this.currentRange.type === "range" &&
265 !("from" in this.currentRange && this.currentRange.from)
266 ) {
267 this.gitService
268 .getBaseCommitRef()
269 .then((baseRef) => {
270 this.currentRange = { type: "range", from: baseRef, to: "HEAD" };
271 this.loadDiffData();
272 })
273 .catch((error) => {
274 console.error("Error getting base commit ref:", error);
275 // Use default range
276 this.loadDiffData();
277 });
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700278 } else {
279 this.loadDiffData();
280 }
281 }
282
283 // Toggle hideUnchangedRegions setting
284 @state()
285 private hideUnchangedRegionsEnabled: boolean = true;
Autoformatter8c463622025-05-16 21:54:17 +0000286
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700287 // Toggle hideUnchangedRegions setting
288 private toggleHideUnchangedRegions() {
289 this.hideUnchangedRegionsEnabled = !this.hideUnchangedRegionsEnabled;
Autoformatter8c463622025-05-16 21:54:17 +0000290
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700291 // Get the Monaco view component
Autoformatter8c463622025-05-16 21:54:17 +0000292 const monacoView = this.shadowRoot?.querySelector("sketch-monaco-view");
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700293 if (monacoView) {
Autoformatter8c463622025-05-16 21:54:17 +0000294 (monacoView as any).toggleHideUnchangedRegions(
295 this.hideUnchangedRegionsEnabled,
296 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700297 }
298 }
Autoformatter8c463622025-05-16 21:54:17 +0000299
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700300 render() {
301 return html`
302 <div class="controls">
303 <div class="controls-container">
304 <div class="range-row">
305 <sketch-diff-range-picker
306 .gitService="${this.gitService}"
307 @range-change="${this.handleRangeChange}"
308 ></sketch-diff-range-picker>
309 </div>
Autoformatter8c463622025-05-16 21:54:17 +0000310
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700311 <div class="file-row">
312 <sketch-diff-file-picker
313 .files="${this.files}"
314 .selectedPath="${this.selectedFilePath}"
315 @file-selected="${this.handleFileSelected}"
316 ></sketch-diff-file-picker>
Autoformatter8c463622025-05-16 21:54:17 +0000317
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700318 <div style="display: flex; gap: 8px;">
Autoformatter8c463622025-05-16 21:54:17 +0000319 ${this.isRightEditable
320 ? html`
321 <div
322 class="editable-indicator"
323 title="This file is editable"
324 >
325 <span
326 style="padding: 6px 12px; background-color: #e9ecef; border-radius: 4px; font-size: 12px; color: #495057;"
327 >
328 Editable
329 </span>
330 </div>
331 `
332 : ""}
333 <button
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700334 class="view-toggle-button"
335 @click="${this.toggleHideUnchangedRegions}"
Autoformatter8c463622025-05-16 21:54:17 +0000336 title="${this.hideUnchangedRegionsEnabled
337 ? "Expand All"
338 : "Hide Unchanged"}"
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700339 >
Autoformatter8c463622025-05-16 21:54:17 +0000340 ${this.hideUnchangedRegionsEnabled
341 ? "Expand All"
342 : "Hide Unchanged"}
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700343 </button>
344 </div>
345 </div>
346 </div>
347 </div>
348
349 <div class="diff-container">
Autoformatter8c463622025-05-16 21:54:17 +0000350 <div class="diff-content">${this.renderDiffContent()}</div>
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700351 </div>
352 `;
353 }
354
355 renderDiffContent() {
356 if (this.loading) {
357 return html`<div class="loading">Loading diff...</div>`;
358 }
359
360 if (this.error) {
361 return html`<div class="error">${this.error}</div>`;
362 }
363
364 if (this.files.length === 0) {
365 return html`<sketch-diff-empty-view></sketch-diff-empty-view>`;
366 }
Autoformatter8c463622025-05-16 21:54:17 +0000367
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700368 if (!this.selectedFilePath) {
369 return html`<div class="loading">Select a file to view diff</div>`;
370 }
371
372 return html`
373 <sketch-monaco-view
374 .originalCode="${this.originalCode}"
375 .modifiedCode="${this.modifiedCode}"
376 .originalFilename="${this.selectedFilePath}"
377 .modifiedFilename="${this.selectedFilePath}"
378 ?readOnly="${!this.isRightEditable}"
379 ?editable-right="${this.isRightEditable}"
380 @monaco-comment="${this.handleMonacoComment}"
381 @monaco-save="${this.handleMonacoSave}"
382 ></sketch-monaco-view>
383 `;
384 }
385
386 /**
387 * Load diff data for the current range
388 */
389 async loadDiffData() {
390 this.loading = true;
391 this.error = null;
392
393 try {
394 // Initialize files as empty array if undefined
395 if (!this.files) {
396 this.files = [];
397 }
398
399 // Load diff data based on the current range type
Autoformatter8c463622025-05-16 21:54:17 +0000400 if (this.currentRange.type === "single") {
401 this.files = await this.gitService.getCommitDiff(
402 this.currentRange.commit,
403 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700404 } else {
Autoformatter8c463622025-05-16 21:54:17 +0000405 this.files = await this.gitService.getDiff(
406 this.currentRange.from,
407 this.currentRange.to,
408 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700409 }
410
411 // Ensure files is always an array, even when API returns null
412 if (!this.files) {
413 this.files = [];
414 }
Autoformatter8c463622025-05-16 21:54:17 +0000415
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700416 // If we have files, select the first one and load its content
417 if (this.files.length > 0) {
418 const firstFile = this.files[0];
419 this.selectedFilePath = firstFile.path;
Autoformatter8c463622025-05-16 21:54:17 +0000420
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700421 // Directly load the file content, especially important when there's only one file
422 // as sometimes the file-selected event might not fire in that case
423 this.loadFileContent(firstFile);
424 } else {
425 // No files to display - reset the view to initial state
Autoformatter8c463622025-05-16 21:54:17 +0000426 this.selectedFilePath = "";
427 this.originalCode = "";
428 this.modifiedCode = "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700429 }
430 } catch (error) {
Autoformatter8c463622025-05-16 21:54:17 +0000431 console.error("Error loading diff data:", error);
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700432 this.error = `Error loading diff data: ${error.message}`;
433 // Ensure files is an empty array when an error occurs
434 this.files = [];
435 // Reset the view to initial state
Autoformatter8c463622025-05-16 21:54:17 +0000436 this.selectedFilePath = "";
437 this.originalCode = "";
438 this.modifiedCode = "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700439 } finally {
440 this.loading = false;
441 }
442 }
443
444 /**
445 * Load the content of the selected file
446 */
447 async loadFileContent(file: GitDiffFile) {
448 this.loading = true;
449 this.error = null;
450
451 try {
452 let fromCommit: string;
453 let toCommit: string;
454 let isUnstagedChanges = false;
Autoformatter8c463622025-05-16 21:54:17 +0000455
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700456 // Determine the commits to compare based on the current range
Autoformatter8c463622025-05-16 21:54:17 +0000457 if (this.currentRange.type === "single") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700458 fromCommit = `${this.currentRange.commit}^`;
459 toCommit = this.currentRange.commit;
460 } else {
461 fromCommit = this.currentRange.from;
462 toCommit = this.currentRange.to;
463 // Check if this is an unstaged changes view
Autoformatter8c463622025-05-16 21:54:17 +0000464 isUnstagedChanges = toCommit === "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700465 }
466
467 // Set editability based on whether we're showing uncommitted changes
468 this.isRightEditable = isUnstagedChanges;
469
470 // Load the original code based on file status
Autoformatter8c463622025-05-16 21:54:17 +0000471 if (file.status === "A") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700472 // Added file: empty original
Autoformatter8c463622025-05-16 21:54:17 +0000473 this.originalCode = "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700474 } else {
475 // For modified, renamed, or deleted files: load original content
Autoformatter8c463622025-05-16 21:54:17 +0000476 this.originalCode = await this.gitService.getFileContent(
477 file.old_hash || "",
478 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700479 }
Autoformatter8c463622025-05-16 21:54:17 +0000480
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700481 // For modified code, always use working copy when editable
482 if (this.isRightEditable) {
483 try {
484 // Always use working copy when editable, regardless of diff status
485 // This ensures we have the latest content even if the diff hasn't been refreshed
Autoformatter8c463622025-05-16 21:54:17 +0000486 this.modifiedCode = await this.gitService.getWorkingCopyContent(
487 file.path,
488 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700489 } catch (error) {
Autoformatter8c463622025-05-16 21:54:17 +0000490 if (file.status === "D") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700491 // For deleted files, silently use empty content
Autoformatter8c463622025-05-16 21:54:17 +0000492 console.warn(
493 `Could not get working copy for deleted file ${file.path}, using empty content`,
494 );
495 this.modifiedCode = "";
Josh Bleecher Snyder4c7865b2025-05-23 17:26:34 +0000496 } else if (file.status === "A" && file.new_hash) {
497 // For added files that don't exist in working directory,
498 // fall back to the committed content
499 console.warn(
500 `Could not get working copy for added file ${file.path}, using committed content`,
501 );
502 this.modifiedCode = await this.gitService.getFileContent(
503 file.new_hash,
504 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700505 } else {
506 // For any other file status, propagate the error
Autoformatter8c463622025-05-16 21:54:17 +0000507 console.error(
508 `Failed to get working copy for ${file.path}:`,
509 error,
510 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700511 throw error; // Rethrow to be caught by the outer try/catch
512 }
513 }
514 } else {
515 // For non-editable view, use git content based on file status
Autoformatter8c463622025-05-16 21:54:17 +0000516 if (file.status === "D") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700517 // Deleted file: empty modified
Autoformatter8c463622025-05-16 21:54:17 +0000518 this.modifiedCode = "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700519 } else {
520 // Added/modified/renamed: use the content from git
Autoformatter8c463622025-05-16 21:54:17 +0000521 this.modifiedCode = await this.gitService.getFileContent(
522 file.new_hash || "",
523 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700524 }
525 }
Autoformatter8c463622025-05-16 21:54:17 +0000526
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700527 // Don't make deleted files editable
Autoformatter8c463622025-05-16 21:54:17 +0000528 if (file.status === "D") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700529 this.isRightEditable = false;
530 }
531 } catch (error) {
Autoformatter8c463622025-05-16 21:54:17 +0000532 console.error("Error loading file content:", error);
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700533 this.error = `Error loading file content: ${error.message}`;
534 this.isRightEditable = false;
535 } finally {
536 this.loading = false;
537 }
538 }
539
540 /**
541 * Handle range change event from the range picker
542 */
543 handleRangeChange(event: CustomEvent) {
544 const { range } = event.detail;
Autoformatter8c463622025-05-16 21:54:17 +0000545 console.log("Range changed:", range);
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700546 this.currentRange = range;
Autoformatter8c463622025-05-16 21:54:17 +0000547
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700548 // Load diff data for the new range
549 this.loadDiffData();
550 }
551
552 /**
553 * Handle file selection event from the file picker
554 */
555 handleFileSelected(event: CustomEvent) {
556 const file = event.detail.file as GitDiffFile;
557 this.selectedFilePath = file.path;
558 this.loadFileContent(file);
559 }
560
561 /**
562 * Refresh the diff view by reloading commits and diff data
Autoformatter8c463622025-05-16 21:54:17 +0000563 *
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700564 * This is called when the Monaco diff tab is activated to ensure:
565 * 1. Branch information from git/recentlog is current (branches can change frequently)
566 * 2. The diff content is synchronized with the latest repository state
567 * 3. Users always see up-to-date information without manual refresh
568 */
569 refreshDiffView() {
570 // First refresh the range picker to get updated branch information
Autoformatter8c463622025-05-16 21:54:17 +0000571 const rangePicker = this.shadowRoot?.querySelector(
572 "sketch-diff-range-picker",
573 );
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700574 if (rangePicker) {
575 (rangePicker as any).loadCommits();
576 }
Autoformatter8c463622025-05-16 21:54:17 +0000577
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700578 if (this.commit) {
Autoformatter8c463622025-05-16 21:54:17 +0000579 this.currentRange = { type: "single", commit: this.commit };
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700580 }
Autoformatter8c463622025-05-16 21:54:17 +0000581
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700582 // Then reload diff data based on the current range
583 this.loadDiffData();
584 }
585}
586
587declare global {
588 interface HTMLElementTagNameMap {
589 "sketch-diff2-view": SketchDiff2View;
590 }
591}