blob: 6a656f32526300d91154df60d954c4de827fe3a3 [file] [log] [blame]
Philip Zeyliger272a90e2025-05-16 14:49:51 -07001// sketch-diff-range-picker.ts
2// Component for selecting commit range for diffs
3
4import { css, html, LitElement } from "lit";
5import { customElement, property, state } from "lit/decorators.js";
philip.zeyliger26bc6592025-06-30 20:15:30 -07006import { GitDataService } from "./git-data-service";
Philip Zeyliger272a90e2025-05-16 14:49:51 -07007import { GitLogEntry } from "../types";
8
9/**
10 * Range type for diff views
11 */
David Crawshaw216d2fc2025-06-15 18:45:53 +000012export type DiffRange = { type: "range"; from: string; to: string };
Philip Zeyliger272a90e2025-05-16 14:49:51 -070013
14/**
15 * Component for selecting commit range for diffs
16 */
17@customElement("sketch-diff-range-picker")
18export class SketchDiffRangePicker extends LitElement {
19 @property({ type: Array })
20 commits: GitLogEntry[] = [];
21
22 @state()
Autoformatter8c463622025-05-16 21:54:17 +000023 private fromCommit: string = "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -070024
25 @state()
Autoformatter8c463622025-05-16 21:54:17 +000026 private toCommit: string = "";
Philip Zeyliger272a90e2025-05-16 14:49:51 -070027
Philip Zeyliger364b35a2025-06-21 16:39:04 -070028 @state()
29 private dropdownOpen: boolean = false;
30
Philip Zeyliger38499cc2025-06-15 21:17:05 -070031 // Removed commitsExpanded state - always expanded now
Philip Zeyliger272a90e2025-05-16 14:49:51 -070032
33 @state()
34 private loading: boolean = true;
35
36 @state()
37 private error: string | null = null;
Autoformatter8c463622025-05-16 21:54:17 +000038
Philip Zeyliger272a90e2025-05-16 14:49:51 -070039 @property({ attribute: false, type: Object })
40 gitService!: GitDataService;
Autoformatter8c463622025-05-16 21:54:17 +000041
Philip Zeyliger272a90e2025-05-16 14:49:51 -070042 constructor() {
43 super();
Autoformatter8c463622025-05-16 21:54:17 +000044 console.log("SketchDiffRangePicker initialized");
Philip Zeyliger272a90e2025-05-16 14:49:51 -070045 }
46
47 static styles = css`
48 :host {
49 display: block;
50 width: 100%;
51 font-family: var(--font-family, system-ui, sans-serif);
52 color: var(--text-color, #333);
53 }
54
55 .range-picker {
56 display: flex;
David Crawshaw216d2fc2025-06-15 18:45:53 +000057 flex-direction: column;
Philip Zeyliger272a90e2025-05-16 14:49:51 -070058 gap: 12px;
Philip Zeyliger272a90e2025-05-16 14:49:51 -070059 width: 100%;
60 box-sizing: border-box;
61 }
62
Philip Zeyliger38499cc2025-06-15 21:17:05 -070063 /* Removed commits-header and commits-label styles - no longer needed */
David Crawshaw216d2fc2025-06-15 18:45:53 +000064
Philip Zeyliger272a90e2025-05-16 14:49:51 -070065 .commit-selectors {
66 display: flex;
67 flex-direction: row;
68 align-items: center;
69 gap: 12px;
70 flex: 1;
Philip Zeyliger272a90e2025-05-16 14:49:51 -070071 }
72
73 .commit-selector {
74 display: flex;
75 align-items: center;
76 gap: 8px;
77 flex: 1;
Philip Zeyliger364b35a2025-06-21 16:39:04 -070078 position: relative;
Philip Zeyliger272a90e2025-05-16 14:49:51 -070079 }
80
Philip Zeyliger364b35a2025-06-21 16:39:04 -070081 /* Custom dropdown styles */
82 .custom-select {
83 position: relative;
84 width: 100%;
85 min-width: 300px;
86 }
87
88 .select-button {
89 width: 100%;
90 padding: 8px 32px 8px 12px;
Philip Zeyliger272a90e2025-05-16 14:49:51 -070091 border: 1px solid var(--border-color, #e0e0e0);
Philip Zeyliger364b35a2025-06-21 16:39:04 -070092 border-radius: 4px;
Philip Zeyliger272a90e2025-05-16 14:49:51 -070093 background-color: var(--background, #fff);
Philip Zeyliger364b35a2025-06-21 16:39:04 -070094 cursor: pointer;
95 text-align: left;
96 display: flex;
97 align-items: center;
98 gap: 8px;
99 min-height: 36px;
100 font-family: inherit;
101 font-size: 14px;
102 position: relative;
103 }
104
105 .select-button:hover {
106 border-color: var(--border-hover, #ccc);
107 }
108
109 .select-button:focus {
110 outline: none;
111 border-color: var(--accent-color, #007acc);
112 box-shadow: 0 0 0 2px var(--accent-color-light, rgba(0, 122, 204, 0.2));
113 }
114
115 .select-button.default-commit {
116 border-color: var(--accent-color, #007acc);
117 background-color: var(--accent-color-light, rgba(0, 122, 204, 0.05));
118 }
119
120 .dropdown-arrow {
121 position: absolute;
122 right: 10px;
123 top: 50%;
124 transform: translateY(-50%);
125 transition: transform 0.2s;
126 }
127
128 .dropdown-arrow.open {
129 transform: translateY(-50%) rotate(180deg);
130 }
131
132 .dropdown-content {
133 position: absolute;
134 top: 100%;
135 left: 0;
136 right: 0;
137 background-color: var(--background, #fff);
138 border: 1px solid var(--border-color, #e0e0e0);
139 border-radius: 4px;
140 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
141 z-index: 1000;
142 max-height: 300px;
143 overflow-y: auto;
144 margin-top: 2px;
145 }
146
147 .dropdown-option {
148 padding: 10px 12px;
149 cursor: pointer;
150 border-bottom: 1px solid var(--border-light, #f0f0f0);
151 display: flex;
152 align-items: flex-start;
153 gap: 8px;
154 font-size: 14px;
155 line-height: 1.4;
156 min-height: auto;
157 }
158
159 .dropdown-option:last-child {
160 border-bottom: none;
161 }
162
163 .dropdown-option:hover {
164 background-color: var(--background-hover, #f5f5f5);
165 }
166
167 .dropdown-option.selected {
168 background-color: var(--accent-color-light, rgba(0, 122, 204, 0.1));
169 }
170
171 .dropdown-option.default-commit {
172 background-color: var(--accent-color-light, rgba(0, 122, 204, 0.05));
173 border-left: 3px solid var(--accent-color, #007acc);
174 padding-left: 9px;
175 }
176
177 .commit-hash {
178 font-family: monospace;
179 color: var(--text-secondary, #666);
180 font-size: 13px;
181 }
182
183 .commit-subject {
184 color: var(--text-primary, #333);
185 flex: 1;
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700186 overflow: hidden;
187 text-overflow: ellipsis;
188 white-space: nowrap;
Philip Zeyliger364b35a2025-06-21 16:39:04 -0700189 min-width: 200px; /* Ensure commit message gets priority */
190 }
191
192 .commit-refs {
193 display: flex;
194 gap: 4px;
195 flex-wrap: wrap;
196 }
197
198 .commit-refs-container {
199 display: flex;
200 gap: 4px;
201 flex-wrap: wrap;
202 flex-shrink: 0;
203 }
204
205 .commit-ref {
206 background-color: var(--tag-bg, #e1f5fe);
207 color: var(--tag-text, #0277bd);
208 padding: 2px 6px;
209 border-radius: 12px;
210 font-size: 11px;
211 font-weight: 500;
212 }
213
214 .commit-ref.branch {
215 background-color: var(--branch-bg, #e8f5e8);
216 color: var(--branch-text, #2e7d32);
217 }
218
219 .commit-ref.tag {
220 background-color: var(--tag-bg, #fff3e0);
221 color: var(--tag-text, #f57c00);
222 }
223
224 .commit-ref.sketch-base {
225 background-color: var(--accent-color, #007acc);
226 color: white;
227 font-weight: 600;
228 }
229
230 .truncated-refs {
231 position: relative;
232 cursor: help;
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700233 }
234
235 label {
236 font-weight: 500;
237 font-size: 14px;
238 }
239
240 .loading {
241 font-style: italic;
242 color: var(--text-muted, #666);
243 }
244
245 .error {
246 color: var(--error-color, #dc3545);
247 font-size: 14px;
248 }
Autoformatter8c463622025-05-16 21:54:17 +0000249
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700250 @media (max-width: 768px) {
251 .commit-selector {
252 max-width: 100%;
253 }
254 }
255 `;
256
257 connectedCallback() {
258 super.connectedCallback();
259 // Wait for DOM to be fully loaded to ensure proper initialization order
Autoformatter8c463622025-05-16 21:54:17 +0000260 if (document.readyState === "complete") {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700261 this.loadCommits();
262 } else {
Autoformatter8c463622025-05-16 21:54:17 +0000263 window.addEventListener("load", () => {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700264 setTimeout(() => this.loadCommits(), 0); // Give time for provider initialization
265 });
266 }
Autoformatter9abf8032025-06-14 23:24:08 +0000267
David Crawshaw938d2dc2025-06-14 22:17:33 +0000268 // Listen for popstate events to handle browser back/forward navigation
Autoformatter9abf8032025-06-14 23:24:08 +0000269 window.addEventListener("popstate", this.handlePopState.bind(this));
David Crawshaw938d2dc2025-06-14 22:17:33 +0000270 }
271
272 disconnectedCallback() {
273 super.disconnectedCallback();
Autoformatter9abf8032025-06-14 23:24:08 +0000274 window.removeEventListener("popstate", this.handlePopState.bind(this));
David Crawshaw938d2dc2025-06-14 22:17:33 +0000275 }
276
277 /**
278 * Handle browser back/forward navigation
279 */
280 private handlePopState() {
281 // Re-initialize from URL parameters when user navigates
282 if (this.commits.length > 0) {
283 const initializedFromUrl = this.initializeFromUrlParams();
284 if (initializedFromUrl) {
285 // Force re-render and dispatch event
286 this.requestUpdate();
287 this.dispatchRangeEvent();
288 }
289 }
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700290 }
291
292 render() {
293 return html`
294 <div class="range-picker">
295 ${this.loading
296 ? html`<div class="loading">Loading commits...</div>`
297 : this.error
Autoformatter8c463622025-05-16 21:54:17 +0000298 ? html`<div class="error">${this.error}</div>`
299 : this.renderRangePicker()}
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700300 </div>
301 `;
302 }
303
304 renderRangePicker() {
305 return html`
Philip Zeyliger38499cc2025-06-15 21:17:05 -0700306 <div class="commit-selectors">${this.renderRangeSelectors()}</div>
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700307 `;
308 }
309
310 renderRangeSelectors() {
Philip Zeyliger364b35a2025-06-21 16:39:04 -0700311 // Always diff against uncommitted changes
312 this.toCommit = "";
313
314 const selectedCommit = this.commits.find((c) => c.hash === this.fromCommit);
315 const isDefaultCommit =
316 selectedCommit && this.isSketchBaseCommit(selectedCommit);
317
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700318 return html`
319 <div class="commit-selector">
Philip Zeyliger364b35a2025-06-21 16:39:04 -0700320 <label for="fromCommit">Diff from:</label>
321 <div class="custom-select" @click=${this.toggleDropdown}>
322 <button
323 class="select-button ${isDefaultCommit ? "default-commit" : ""}"
324 @click=${this.toggleDropdown}
325 @blur=${this.handleBlur}
326 >
327 ${selectedCommit
328 ? this.renderCommitButton(selectedCommit)
329 : "Select commit..."}
330 <svg
331 class="dropdown-arrow ${this.dropdownOpen ? "open" : ""}"
332 width="12"
333 height="12"
334 viewBox="0 0 12 12"
335 >
336 <path d="M6 8l-4-4h8z" fill="currentColor" />
337 </svg>
338 </button>
339 ${this.dropdownOpen
340 ? html`
341 <div class="dropdown-content">
342 ${this.commits.map(
343 (commit) => html`
344 <div
345 class="dropdown-option ${commit.hash === this.fromCommit
346 ? "selected"
347 : ""} ${this.isSketchBaseCommit(commit)
348 ? "default-commit"
349 : ""}"
350 @click=${() => this.selectCommit(commit.hash)}
351 >
352 ${this.renderCommitOption(commit)}
353 </div>
354 `,
355 )}
356 </div>
357 `
358 : ""}
359 </div>
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700360 </div>
361 `;
362 }
363
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700364 /**
Philip Zeyliger364b35a2025-06-21 16:39:04 -0700365 * Format a commit for display in the dropdown (legacy method, kept for compatibility)
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700366 */
367 formatCommitOption(commit: GitLogEntry): string {
368 const shortHash = commit.hash.substring(0, 7);
Autoformatter8c463622025-05-16 21:54:17 +0000369
David Crawshawdbca8972025-06-14 23:46:58 +0000370 // Truncate subject if it's too long
371 let subject = commit.subject;
372 if (subject.length > 50) {
373 subject = subject.substring(0, 47) + "...";
374 }
375
376 let label = `${shortHash} ${subject}`;
377
378 // Add refs but keep them concise
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700379 if (commit.refs && commit.refs.length > 0) {
David Crawshawdbca8972025-06-14 23:46:58 +0000380 const refs = commit.refs.map((ref) => {
381 // Shorten common prefixes
382 if (ref.startsWith("origin/")) {
383 return ref.substring(7);
384 }
385 if (ref.startsWith("refs/heads/")) {
386 return ref.substring(11);
387 }
388 if (ref.startsWith("refs/remotes/origin/")) {
389 return ref.substring(20);
390 }
391 return ref;
392 });
393
394 // Limit to first 2 refs to avoid overcrowding
395 const displayRefs = refs.slice(0, 2);
396 if (refs.length > 2) {
397 displayRefs.push(`+${refs.length - 2} more`);
398 }
399
400 label += ` (${displayRefs.join(", ")})`;
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700401 }
Autoformatter8c463622025-05-16 21:54:17 +0000402
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700403 return label;
404 }
405
406 /**
407 * Load commits from the Git data service
408 */
409 async loadCommits() {
410 this.loading = true;
411 this.error = null;
412
413 if (!this.gitService) {
Autoformatter8c463622025-05-16 21:54:17 +0000414 console.error("GitService was not provided to sketch-diff-range-picker");
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700415 throw Error();
416 }
417
418 try {
419 // Get the base commit reference
420 const baseCommitRef = await this.gitService.getBaseCommitRef();
Autoformatter8c463622025-05-16 21:54:17 +0000421
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700422 // Load commit history
423 this.commits = await this.gitService.getCommitHistory(baseCommitRef);
Autoformatter8c463622025-05-16 21:54:17 +0000424
David Crawshaw938d2dc2025-06-14 22:17:33 +0000425 // Check if we should initialize from URL parameters first
426 const initializedFromUrl = this.initializeFromUrlParams();
Autoformatter9abf8032025-06-14 23:24:08 +0000427
David Crawshaw938d2dc2025-06-14 22:17:33 +0000428 // Set default selections only if not initialized from URL
429 if (this.commits.length > 0 && !initializedFromUrl) {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700430 // For range, default is base to HEAD
431 // TODO: is sketch-base right in the unsafe context, where it's sketch-base-...
432 // should this be startswith?
Autoformatter8c463622025-05-16 21:54:17 +0000433 const baseCommit = this.commits.find(
434 (c) => c.refs && c.refs.some((ref) => ref.includes("sketch-base")),
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700435 );
Autoformatter8c463622025-05-16 21:54:17 +0000436
437 this.fromCommit = baseCommit
438 ? baseCommit.hash
439 : this.commits[this.commits.length - 1].hash;
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700440 // Default to Uncommitted Changes by setting toCommit to empty string
Autoformatter8c463622025-05-16 21:54:17 +0000441 this.toCommit = ""; // Empty string represents uncommitted changes
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700442 }
Autoformatter9abf8032025-06-14 23:24:08 +0000443
David Crawshaw938d2dc2025-06-14 22:17:33 +0000444 // Always dispatch range event to ensure diff view is updated
445 this.dispatchRangeEvent();
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700446 } catch (error) {
Autoformatter8c463622025-05-16 21:54:17 +0000447 console.error("Error loading commits:", error);
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700448 this.error = `Error loading commits: ${error.message}`;
449 } finally {
450 this.loading = false;
451 }
452 }
453
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700454 /**
455 * Handle From commit change
456 */
457 handleFromChange(event: Event) {
458 const select = event.target as HTMLSelectElement;
459 this.fromCommit = select.value;
460 this.dispatchRangeEvent();
461 }
462
463 /**
464 * Handle To commit change
465 */
466 handleToChange(event: Event) {
467 const select = event.target as HTMLSelectElement;
468 this.toCommit = select.value;
469 this.dispatchRangeEvent();
470 }
471
Philip Zeyliger364b35a2025-06-21 16:39:04 -0700472 /**
473 * Toggle dropdown open/closed
474 */
475 toggleDropdown(event: Event) {
476 event.stopPropagation();
477 this.dropdownOpen = !this.dropdownOpen;
478
479 if (this.dropdownOpen) {
480 // Close dropdown when clicking outside
481 setTimeout(() => {
482 document.addEventListener("click", this.closeDropdown, { once: true });
483 }, 0);
484 }
485 }
486
487 /**
488 * Close dropdown
489 */
490 closeDropdown = () => {
491 this.dropdownOpen = false;
492 };
493
494 /**
495 * Handle blur event on select button
496 */
philip.zeyliger26bc6592025-06-30 20:15:30 -0700497 handleBlur(_event: FocusEvent) {
Philip Zeyliger364b35a2025-06-21 16:39:04 -0700498 // Small delay to allow click events to process
499 setTimeout(() => {
500 if (!this.shadowRoot?.activeElement?.closest(".custom-select")) {
501 this.dropdownOpen = false;
502 }
503 }, 150);
504 }
505
506 /**
507 * Select a commit from dropdown
508 */
509 selectCommit(hash: string) {
510 this.fromCommit = hash;
511 this.dropdownOpen = false;
512 this.dispatchRangeEvent();
513 }
514
515 /**
516 * Check if a commit is a sketch-base commit
517 */
518 isSketchBaseCommit(commit: GitLogEntry): boolean {
519 return commit.refs?.some((ref) => ref.includes("sketch-base")) || false;
520 }
521
522 /**
523 * Render commit for the dropdown button
524 */
525 renderCommitButton(commit: GitLogEntry) {
526 const shortHash = commit.hash.substring(0, 7);
527 let subject = commit.subject;
528 if (subject.length > 40) {
529 subject = subject.substring(0, 37) + "...";
530 }
531
532 return html`
533 <span class="commit-hash">${shortHash}</span>
534 <span class="commit-subject">${subject}</span>
535 ${this.isSketchBaseCommit(commit)
536 ? html` <span class="commit-ref sketch-base">base</span> `
537 : ""}
538 `;
539 }
540
541 /**
542 * Render commit option in dropdown
543 */
544 renderCommitOption(commit: GitLogEntry) {
545 const shortHash = commit.hash.substring(0, 7);
546 let subject = commit.subject;
547 if (subject.length > 50) {
548 subject = subject.substring(0, 47) + "...";
549 }
550
551 return html`
552 <span class="commit-hash">${shortHash}</span>
553 <span class="commit-subject">${subject}</span>
554 ${commit.refs && commit.refs.length > 0
555 ? html` <div class="commit-refs">${this.renderRefs(commit.refs)}</div> `
556 : ""}
557 `;
558 }
559
560 /**
561 * Render all refs naturally without truncation
562 */
563 renderRefs(refs: string[]) {
564 return html`
565 <div class="commit-refs-container">
566 ${refs.map((ref) => {
567 const shortRef = this.getShortRefName(ref);
568 const isSketchBase = ref.includes("sketch-base");
569 const refClass = isSketchBase
570 ? "sketch-base"
571 : ref.includes("tag")
572 ? "tag"
573 : "branch";
574 return html`<span class="commit-ref ${refClass}">${shortRef}</span>`;
575 })}
576 </div>
577 `;
578 }
579
580 /**
581 * Get shortened reference name for compact display
582 */
583 getShortRefName(ref: string): string {
584 if (ref.startsWith("refs/heads/")) {
585 return ref.substring(11);
586 }
587 if (ref.startsWith("refs/remotes/origin/")) {
588 return ref.substring(20);
589 }
590 if (ref.startsWith("refs/tags/")) {
591 return ref.substring(10);
592 }
593 return ref;
594 }
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700595
596 /**
David Crawshaw216d2fc2025-06-15 18:45:53 +0000597 * Get a summary of the current commit range for display
Philip Zeyligere89b3082025-05-29 03:16:06 +0000598 */
David Crawshaw216d2fc2025-06-15 18:45:53 +0000599 getCommitSummary(): string {
600 if (!this.fromCommit && !this.toCommit) {
Autoformatter62554112025-06-15 19:23:33 +0000601 return "No commits selected";
David Crawshaw216d2fc2025-06-15 18:45:53 +0000602 }
603
Autoformatter62554112025-06-15 19:23:33 +0000604 const fromShort = this.fromCommit ? this.fromCommit.substring(0, 7) : "";
605 const toShort = this.toCommit
606 ? this.toCommit.substring(0, 7)
607 : "Uncommitted";
608
David Crawshaw216d2fc2025-06-15 18:45:53 +0000609 return `${fromShort}..${toShort}`;
Philip Zeyligere89b3082025-05-29 03:16:06 +0000610 }
611
612 /**
David Crawshaw938d2dc2025-06-14 22:17:33 +0000613 * Validate that a commit hash exists in the loaded commits
614 */
615 private isValidCommitHash(hash: string): boolean {
Autoformatter9abf8032025-06-14 23:24:08 +0000616 if (!hash || hash.trim() === "") return true; // Empty is valid (uncommitted changes)
617 return this.commits.some(
618 (commit) => commit.hash.startsWith(hash) || commit.hash === hash,
619 );
David Crawshaw938d2dc2025-06-14 22:17:33 +0000620 }
621
622 /**
623 * Dispatch range change event and update URL parameters
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700624 */
625 dispatchRangeEvent() {
Autoformatter62554112025-06-15 19:23:33 +0000626 const range: DiffRange = {
627 type: "range",
628 from: this.fromCommit,
629 to: this.toCommit,
630 };
Autoformatter8c463622025-05-16 21:54:17 +0000631
David Crawshaw938d2dc2025-06-14 22:17:33 +0000632 // Update URL parameters
633 this.updateUrlParams(range);
634
Autoformatter8c463622025-05-16 21:54:17 +0000635 const event = new CustomEvent("range-change", {
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700636 detail: { range },
637 bubbles: true,
Autoformatter8c463622025-05-16 21:54:17 +0000638 composed: true,
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700639 });
Autoformatter8c463622025-05-16 21:54:17 +0000640
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700641 this.dispatchEvent(event);
642 }
David Crawshaw938d2dc2025-06-14 22:17:33 +0000643
644 /**
645 * Update URL parameters for from and to commits
646 */
647 private updateUrlParams(range: DiffRange) {
648 const url = new URL(window.location.href);
Autoformatter9abf8032025-06-14 23:24:08 +0000649
David Crawshaw938d2dc2025-06-14 22:17:33 +0000650 // Remove existing range parameters
Autoformatter9abf8032025-06-14 23:24:08 +0000651 url.searchParams.delete("from");
652 url.searchParams.delete("to");
653 url.searchParams.delete("commit");
654
David Crawshaw216d2fc2025-06-15 18:45:53 +0000655 // Add from parameter if not empty
656 if (range.from && range.from.trim() !== "") {
657 url.searchParams.set("from", range.from);
658 }
659 // Add to parameter if not empty (empty string means uncommitted changes)
660 if (range.to && range.to.trim() !== "") {
661 url.searchParams.set("to", range.to);
David Crawshaw938d2dc2025-06-14 22:17:33 +0000662 }
Autoformatter9abf8032025-06-14 23:24:08 +0000663
David Crawshaw938d2dc2025-06-14 22:17:33 +0000664 // Update the browser history without reloading the page
Autoformatter9abf8032025-06-14 23:24:08 +0000665 window.history.replaceState(window.history.state, "", url.toString());
David Crawshaw938d2dc2025-06-14 22:17:33 +0000666 }
667
668 /**
669 * Initialize from URL parameters if available
670 */
671 private initializeFromUrlParams() {
672 const url = new URL(window.location.href);
Autoformatter9abf8032025-06-14 23:24:08 +0000673 const fromParam = url.searchParams.get("from");
674 const toParam = url.searchParams.get("to");
Autoformatter9abf8032025-06-14 23:24:08 +0000675
David Crawshaw216d2fc2025-06-15 18:45:53 +0000676 // If from or to parameters are present, use them
David Crawshaw938d2dc2025-06-14 22:17:33 +0000677 if (fromParam || toParam) {
David Crawshaw938d2dc2025-06-14 22:17:33 +0000678 if (fromParam) {
679 this.fromCommit = fromParam;
680 }
681 if (toParam) {
682 this.toCommit = toParam;
683 } else {
684 // If no 'to' param, default to uncommitted changes (empty string)
Autoformatter9abf8032025-06-14 23:24:08 +0000685 this.toCommit = "";
David Crawshaw938d2dc2025-06-14 22:17:33 +0000686 }
687 return true; // Indicate that we initialized from URL
688 }
Autoformatter9abf8032025-06-14 23:24:08 +0000689
David Crawshaw938d2dc2025-06-14 22:17:33 +0000690 return false; // No URL params found
691 }
Philip Zeyliger272a90e2025-05-16 14:49:51 -0700692}
693
694declare global {
695 interface HTMLElementTagNameMap {
696 "sketch-diff-range-picker": SketchDiffRangePicker;
697 }
698}