ciyidogan commited on
Commit
960f82f
·
verified ·
1 Parent(s): bb05395

Upload version-compare-dialog.component.ts

Browse files
flare-ui/src/app/dialogs/version-compare-dialog/version-compare-dialog.component.ts ADDED
@@ -0,0 +1,611 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, Inject } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
4
+ import { MatSelectModule } from '@angular/material/select';
5
+ import { MatButtonModule } from '@angular/material/button';
6
+ import { MatIconModule } from '@angular/material/icon';
7
+ import { MatChipsModule } from '@angular/material/chips';
8
+ import { MatExpansionModule } from '@angular/material/expansion';
9
+ import { MatDividerModule } from '@angular/material/divider';
10
+ import { MatListModule } from '@angular/material/list';
11
+ import { FormsModule } from '@angular/forms';
12
+ import { Version } from '../../services/api.service';
13
+
14
+ interface Difference {
15
+ field: string;
16
+ label: string;
17
+ v1Value: any;
18
+ v2Value: any;
19
+ type: 'added' | 'removed' | 'modified' | 'unchanged';
20
+ }
21
+
22
+ @Component({
23
+ selector: 'app-version-compare-dialog',
24
+ standalone: true,
25
+ imports: [
26
+ CommonModule,
27
+ FormsModule,
28
+ MatDialogModule,
29
+ MatSelectModule,
30
+ MatButtonModule,
31
+ MatIconModule,
32
+ MatChipsModule,
33
+ MatExpansionModule,
34
+ MatDividerModule,
35
+ MatListModule
36
+ ],
37
+ template: `
38
+ <h2 mat-dialog-title>Compare Versions</h2>
39
+
40
+ <mat-dialog-content>
41
+ <div class="compare-container">
42
+ <!-- Version Selectors -->
43
+ <div class="version-selectors">
44
+ <mat-form-field appearance="outline">
45
+ <mat-label>Version 1</mat-label>
46
+ <mat-select [(value)]="version1" (selectionChange)="compareVersions()">
47
+ <mat-option *ngFor="let v of versions" [value]="v">
48
+ Version {{ v.id }} - {{ v.caption }}
49
+ <span class="published-marker" *ngIf="v.published">(Published)</span>
50
+ </mat-option>
51
+ </mat-select>
52
+ </mat-form-field>
53
+
54
+ <mat-icon class="compare-icon">compare_arrows</mat-icon>
55
+
56
+ <mat-form-field appearance="outline">
57
+ <mat-label>Version 2</mat-label>
58
+ <mat-select [(value)]="version2" (selectionChange)="compareVersions()">
59
+ <mat-option *ngFor="let v of versions" [value]="v">
60
+ Version {{ v.id }} - {{ v.caption }}
61
+ <span class="published-marker" *ngIf="v.published">(Published)</span>
62
+ </mat-option>
63
+ </mat-select>
64
+ </mat-form-field>
65
+ </div>
66
+
67
+ <!-- Comparison Results -->
68
+ <div class="comparison-results" *ngIf="differences.length > 0">
69
+
70
+ <!-- Summary -->
71
+ <div class="summary-chips">
72
+ <mat-chip-listbox>
73
+ <mat-chip-option selected>
74
+ <mat-icon>add_circle</mat-icon>
75
+ {{ addedCount }} Added
76
+ </mat-chip-option>
77
+ <mat-chip-option selected color="warn">
78
+ <mat-icon>remove_circle</mat-icon>
79
+ {{ removedCount }} Removed
80
+ </mat-chip-option>
81
+ <mat-chip-option selected color="accent">
82
+ <mat-icon>edit</mat-icon>
83
+ {{ modifiedCount }} Modified
84
+ </mat-chip-option>
85
+ </mat-chip-listbox>
86
+ </div>
87
+
88
+ <!-- General Differences -->
89
+ <mat-expansion-panel [expanded]="hasGeneralDifferences">
90
+ <mat-expansion-panel-header>
91
+ <mat-panel-title>
92
+ General Configuration
93
+ </mat-panel-title>
94
+ <mat-panel-description>
95
+ {{ generalDifferences.length }} differences
96
+ </mat-panel-description>
97
+ </mat-expansion-panel-header>
98
+
99
+ <mat-list>
100
+ <mat-list-item *ngFor="let diff of generalDifferences">
101
+ <mat-icon matListItemIcon [class]="'diff-' + diff.type">
102
+ {{ getDiffIcon(diff.type) }}
103
+ </mat-icon>
104
+ <div matListItemTitle>{{ diff.label }}</div>
105
+ <div matListItemLine class="diff-values">
106
+ <span class="old-value" *ngIf="diff.type !== 'added'">{{ formatValue(diff.v1Value) }}</span>
107
+ <mat-icon *ngIf="diff.type === 'modified'">arrow_forward</mat-icon>
108
+ <span class="new-value" *ngIf="diff.type !== 'removed'">{{ formatValue(diff.v2Value) }}</span>
109
+ </div>
110
+ </mat-list-item>
111
+ </mat-list>
112
+ </mat-expansion-panel>
113
+
114
+ <!-- LLM Differences -->
115
+ <mat-expansion-panel [expanded]="hasLLMDifferences">
116
+ <mat-expansion-panel-header>
117
+ <mat-panel-title>
118
+ LLM Configuration
119
+ </mat-panel-title>
120
+ <mat-panel-description>
121
+ {{ llmDifferences.length }} differences
122
+ </mat-panel-description>
123
+ </mat-expansion-panel-header>
124
+
125
+ <mat-list>
126
+ <mat-list-item *ngFor="let diff of llmDifferences">
127
+ <mat-icon matListItemIcon [class]="'diff-' + diff.type">
128
+ {{ getDiffIcon(diff.type) }}
129
+ </mat-icon>
130
+ <div matListItemTitle>{{ diff.label }}</div>
131
+ <div matListItemLine class="diff-values">
132
+ <span class="old-value" *ngIf="diff.type !== 'added'">{{ formatValue(diff.v1Value) }}</span>
133
+ <mat-icon *ngIf="diff.type === 'modified'">arrow_forward</mat-icon>
134
+ <span class="new-value" *ngIf="diff.type !== 'removed'">{{ formatValue(diff.v2Value) }}</span>
135
+ </div>
136
+ </mat-list-item>
137
+ </mat-list>
138
+ </mat-expansion-panel>
139
+
140
+ <!-- Intent Differences -->
141
+ <mat-expansion-panel [expanded]="hasIntentDifferences">
142
+ <mat-expansion-panel-header>
143
+ <mat-panel-title>
144
+ Intents
145
+ </mat-panel-title>
146
+ <mat-panel-description>
147
+ {{ intentDifferences.added.length + intentDifferences.removed.length + intentDifferences.modified.length }} differences
148
+ </mat-panel-description>
149
+ </mat-expansion-panel-header>
150
+
151
+ <div class="intents-comparison">
152
+ <!-- Added Intents -->
153
+ <div class="intent-group" *ngIf="intentDifferences.added.length > 0">
154
+ <h4><mat-icon>add_circle</mat-icon> Added Intents</h4>
155
+ <mat-list>
156
+ <mat-list-item *ngFor="let intent of intentDifferences.added">
157
+ <mat-icon matListItemIcon class="diff-added">add</mat-icon>
158
+ <div matListItemTitle>{{ intent.name }}</div>
159
+ <div matListItemLine>{{ intent.caption || 'No description' }}</div>
160
+ </mat-list-item>
161
+ </mat-list>
162
+ </div>
163
+
164
+ <!-- Removed Intents -->
165
+ <div class="intent-group" *ngIf="intentDifferences.removed.length > 0">
166
+ <h4><mat-icon>remove_circle</mat-icon> Removed Intents</h4>
167
+ <mat-list>
168
+ <mat-list-item *ngFor="let intent of intentDifferences.removed">
169
+ <mat-icon matListItemIcon class="diff-removed">remove</mat-icon>
170
+ <div matListItemTitle>{{ intent.name }}</div>
171
+ <div matListItemLine>{{ intent.caption || 'No description' }}</div>
172
+ </mat-list-item>
173
+ </mat-list>
174
+ </div>
175
+
176
+ <!-- Modified Intents -->
177
+ <div class="intent-group" *ngIf="intentDifferences.modified.length > 0">
178
+ <h4><mat-icon>edit</mat-icon> Modified Intents</h4>
179
+ <mat-expansion-panel *ngFor="let intent of intentDifferences.modified">
180
+ <mat-expansion-panel-header>
181
+ <mat-panel-title>{{ intent.name }}</mat-panel-title>
182
+ <mat-panel-description>{{ intent.changes.length }} changes</mat-panel-description>
183
+ </mat-expansion-panel-header>
184
+
185
+ <mat-list>
186
+ <mat-list-item *ngFor="let change of intent.changes">
187
+ <mat-icon matListItemIcon [class]="'diff-' + change.type">
188
+ {{ getDiffIcon(change.type) }}
189
+ </mat-icon>
190
+ <div matListItemTitle>{{ change.label }}</div>
191
+ <div matListItemLine class="diff-values">
192
+ <span class="old-value" *ngIf="change.type !== 'added'">{{ formatValue(change.v1Value) }}</span>
193
+ <mat-icon *ngIf="change.type === 'modified'">arrow_forward</mat-icon>
194
+ <span class="new-value" *ngIf="change.type !== 'removed'">{{ formatValue(change.v2Value) }}</span>
195
+ </div>
196
+ </mat-list-item>
197
+ </mat-list>
198
+ </mat-expansion-panel>
199
+ </div>
200
+ </div>
201
+ </mat-expansion-panel>
202
+
203
+ </div>
204
+
205
+ <!-- No Selection State -->
206
+ <div class="empty-state" *ngIf="!version1 || !version2">
207
+ <mat-icon>compare</mat-icon>
208
+ <p>Select two versions to compare</p>
209
+ </div>
210
+
211
+ <!-- Same Version State -->
212
+ <div class="empty-state" *ngIf="version1 && version2 && version1.id === version2.id">
213
+ <mat-icon>info</mat-icon>
214
+ <p>Please select different versions to compare</p>
215
+ </div>
216
+
217
+ <!-- No Differences State -->
218
+ <div class="empty-state" *ngIf="version1 && version2 && version1.id !== version2.id && differences.length === 0">
219
+ <mat-icon>check_circle</mat-icon>
220
+ <p>These versions are identical</p>
221
+ </div>
222
+ </div>
223
+ </mat-dialog-content>
224
+
225
+ <mat-dialog-actions align="end">
226
+ <button mat-button (click)="close()">Close</button>
227
+ </mat-dialog-actions>
228
+ `,
229
+ styles: [`
230
+ .compare-container {
231
+ min-width: 800px;
232
+ max-width: 1000px;
233
+ }
234
+
235
+ .version-selectors {
236
+ display: flex;
237
+ gap: 24px;
238
+ align-items: center;
239
+ justify-content: center;
240
+ margin-bottom: 32px;
241
+
242
+ mat-form-field {
243
+ flex: 1;
244
+ max-width: 350px;
245
+ }
246
+
247
+ .compare-icon {
248
+ font-size: 32px;
249
+ width: 32px;
250
+ height: 32px;
251
+ color: #666;
252
+ }
253
+
254
+ .published-marker {
255
+ color: #4caf50;
256
+ font-weight: 500;
257
+ margin-left: 8px;
258
+ }
259
+ }
260
+
261
+ .summary-chips {
262
+ margin-bottom: 24px;
263
+ display: flex;
264
+ justify-content: center;
265
+
266
+ mat-chip {
267
+ margin: 0 4px;
268
+
269
+ mat-icon {
270
+ margin-right: 4px;
271
+ }
272
+ }
273
+ }
274
+
275
+ .comparison-results {
276
+ mat-expansion-panel {
277
+ margin-bottom: 16px;
278
+ }
279
+ }
280
+
281
+ .diff-values {
282
+ display: flex;
283
+ align-items: center;
284
+ gap: 8px;
285
+ margin-top: 4px;
286
+
287
+ .old-value {
288
+ color: #d32f2f;
289
+ text-decoration: line-through;
290
+ }
291
+
292
+ .new-value {
293
+ color: #388e3c;
294
+ font-weight: 500;
295
+ }
296
+
297
+ mat-icon {
298
+ font-size: 16px;
299
+ width: 16px;
300
+ height: 16px;
301
+ color: #666;
302
+ }
303
+ }
304
+
305
+ .diff-added {
306
+ color: #388e3c;
307
+ }
308
+
309
+ .diff-removed {
310
+ color: #d32f2f;
311
+ }
312
+
313
+ .diff-modified {
314
+ color: #1976d2;
315
+ }
316
+
317
+ .intents-comparison {
318
+ .intent-group {
319
+ margin-bottom: 24px;
320
+
321
+ h4 {
322
+ display: flex;
323
+ align-items: center;
324
+ gap: 8px;
325
+ margin-bottom: 12px;
326
+ color: #666;
327
+
328
+ mat-icon {
329
+ font-size: 20px;
330
+ width: 20px;
331
+ height: 20px;
332
+ }
333
+ }
334
+
335
+ mat-expansion-panel {
336
+ margin-bottom: 8px;
337
+ }
338
+ }
339
+ }
340
+
341
+ .empty-state {
342
+ text-align: center;
343
+ padding: 60px 20px;
344
+
345
+ mat-icon {
346
+ font-size: 64px;
347
+ width: 64px;
348
+ height: 64px;
349
+ color: #e0e0e0;
350
+ margin-bottom: 16px;
351
+ }
352
+
353
+ p {
354
+ color: #666;
355
+ font-size: 16px;
356
+ }
357
+ }
358
+ `]
359
+ })
360
+ export default class VersionCompareDialogComponent {
361
+ versions: Version[];
362
+ version1: Version | null = null;
363
+ version2: Version | null = null;
364
+
365
+ differences: Difference[] = [];
366
+ generalDifferences: Difference[] = [];
367
+ llmDifferences: Difference[] = [];
368
+ intentDifferences = {
369
+ added: [] as any[],
370
+ removed: [] as any[],
371
+ modified: [] as any[]
372
+ };
373
+
374
+ constructor(
375
+ public dialogRef: MatDialogRef<VersionCompareDialogComponent>,
376
+ @Inject(MAT_DIALOG_DATA) public data: { versions: Version[], selectedVersion?: Version }
377
+ ) {
378
+ this.versions = data.versions;
379
+
380
+ // Pre-select versions
381
+ if (data.selectedVersion) {
382
+ this.version1 = data.selectedVersion;
383
+ // Select the next most recent version as version2
384
+ const otherVersions = this.versions.filter(v => v.id !== data.selectedVersion!.id);
385
+ if (otherVersions.length > 0) {
386
+ this.version2 = otherVersions[0];
387
+ this.compareVersions();
388
+ }
389
+ }
390
+ }
391
+
392
+ get addedCount(): number {
393
+ return this.differences.filter(d => d.type === 'added').length + this.intentDifferences.added.length;
394
+ }
395
+
396
+ get removedCount(): number {
397
+ return this.differences.filter(d => d.type === 'removed').length + this.intentDifferences.removed.length;
398
+ }
399
+
400
+ get modifiedCount(): number {
401
+ return this.differences.filter(d => d.type === 'modified').length + this.intentDifferences.modified.length;
402
+ }
403
+
404
+ get hasGeneralDifferences(): boolean {
405
+ return this.generalDifferences.length > 0;
406
+ }
407
+
408
+ get hasLLMDifferences(): boolean {
409
+ return this.llmDifferences.length > 0;
410
+ }
411
+
412
+ get hasIntentDifferences(): boolean {
413
+ return this.intentDifferences.added.length > 0 ||
414
+ this.intentDifferences.removed.length > 0 ||
415
+ this.intentDifferences.modified.length > 0;
416
+ }
417
+
418
+ compareVersions() {
419
+ if (!this.version1 || !this.version2 || this.version1.id === this.version2.id) {
420
+ this.differences = [];
421
+ this.generalDifferences = [];
422
+ this.llmDifferences = [];
423
+ this.intentDifferences = { added: [], removed: [], modified: [] };
424
+ return;
425
+ }
426
+
427
+ this.differences = [];
428
+
429
+ // Compare general fields
430
+ this.compareField('caption', 'Caption', this.version1.caption, this.version2.caption);
431
+ this.compareField('general_prompt', 'General Prompt',
432
+ (this.version1 as any).general_prompt,
433
+ (this.version2 as any).general_prompt);
434
+ this.compareField('published', 'Published Status', this.version1.published, this.version2.published);
435
+
436
+ // Compare LLM configuration
437
+ if (this.version1.llm && this.version2.llm) {
438
+ this.compareField('llm.repo_id', 'Model Repository',
439
+ this.version1.llm.repo_id,
440
+ this.version2.llm.repo_id);
441
+ this.compareField('llm.use_fine_tune', 'Use Fine-Tune',
442
+ this.version1.llm.use_fine_tune,
443
+ this.version2.llm.use_fine_tune);
444
+
445
+ if (this.version1.llm.use_fine_tune || this.version2.llm.use_fine_tune) {
446
+ this.compareField('llm.fine_tune_zip', 'Fine-Tune ZIP',
447
+ this.version1.llm.fine_tune_zip,
448
+ this.version2.llm.fine_tune_zip);
449
+ }
450
+
451
+ // Compare generation config
452
+ const gc1 = this.version1.llm.generation_config;
453
+ const gc2 = this.version2.llm.generation_config;
454
+ if (gc1 && gc2) {
455
+ this.compareField('llm.generation_config.max_new_tokens', 'Max Tokens',
456
+ gc1.max_new_tokens, gc2.max_new_tokens);
457
+ this.compareField('llm.generation_config.temperature', 'Temperature',
458
+ gc1.temperature, gc2.temperature);
459
+ this.compareField('llm.generation_config.top_p', 'Top P',
460
+ gc1.top_p, gc2.top_p);
461
+ this.compareField('llm.generation_config.repetition_penalty', 'Repetition Penalty',
462
+ gc1.repetition_penalty, gc2.repetition_penalty);
463
+ }
464
+ }
465
+
466
+ // Compare intents
467
+ this.compareIntents();
468
+
469
+ // Categorize differences
470
+ this.generalDifferences = this.differences.filter(d =>
471
+ !d.field.startsWith('llm.') && !d.field.startsWith('intent.'));
472
+ this.llmDifferences = this.differences.filter(d => d.field.startsWith('llm.'));
473
+ }
474
+
475
+ private compareField(field: string, label: string, v1Value: any, v2Value: any) {
476
+ if (v1Value === v2Value) {
477
+ return;
478
+ }
479
+
480
+ let type: 'added' | 'removed' | 'modified';
481
+ if (v1Value === undefined || v1Value === null || v1Value === '') {
482
+ type = 'added';
483
+ } else if (v2Value === undefined || v2Value === null || v2Value === '') {
484
+ type = 'removed';
485
+ } else {
486
+ type = 'modified';
487
+ }
488
+
489
+ this.differences.push({
490
+ field,
491
+ label,
492
+ v1Value,
493
+ v2Value,
494
+ type
495
+ });
496
+ }
497
+
498
+ private compareIntents() {
499
+ const intents1 = this.version1?.intents || [];
500
+ const intents2 = this.version2?.intents || [];
501
+
502
+ const intents1Map = new Map(intents1.map(i => [i.name, i]));
503
+ const intents2Map = new Map(intents2.map(i => [i.name, i]));
504
+
505
+ // Find added intents
506
+ this.intentDifferences.added = intents2.filter(i => !intents1Map.has(i.name));
507
+
508
+ // Find removed intents
509
+ this.intentDifferences.removed = intents1.filter(i => !intents2Map.has(i.name));
510
+
511
+ // Find modified intents
512
+ this.intentDifferences.modified = [];
513
+ for (const [name, intent1] of intents1Map) {
514
+ const intent2 = intents2Map.get(name);
515
+ if (intent2) {
516
+ const changes = this.compareIntentDetails(intent1, intent2);
517
+ if (changes.length > 0) {
518
+ this.intentDifferences.modified.push({
519
+ name,
520
+ changes
521
+ });
522
+ }
523
+ }
524
+ }
525
+ }
526
+
527
+ private compareIntentDetails(intent1: any, intent2: any): Difference[] {
528
+ const changes: Difference[] = [];
529
+
530
+ // Compare basic fields
531
+ if (intent1.caption !== intent2.caption) {
532
+ changes.push({
533
+ field: `intent.${intent1.name}.caption`,
534
+ label: 'Caption',
535
+ v1Value: intent1.caption,
536
+ v2Value: intent2.caption,
537
+ type: 'modified'
538
+ });
539
+ }
540
+
541
+ if (intent1.detection_prompt !== intent2.detection_prompt) {
542
+ changes.push({
543
+ field: `intent.${intent1.name}.detection_prompt`,
544
+ label: 'Detection Prompt',
545
+ v1Value: intent1.detection_prompt,
546
+ v2Value: intent2.detection_prompt,
547
+ type: 'modified'
548
+ });
549
+ }
550
+
551
+ if (intent1.action !== intent2.action) {
552
+ changes.push({
553
+ field: `intent.${intent1.name}.action`,
554
+ label: 'API Action',
555
+ v1Value: intent1.action,
556
+ v2Value: intent2.action,
557
+ type: 'modified'
558
+ });
559
+ }
560
+
561
+ // Compare examples
562
+ const examples1 = intent1.examples || [];
563
+ const examples2 = intent2.examples || [];
564
+ if (JSON.stringify(examples1) !== JSON.stringify(examples2)) {
565
+ changes.push({
566
+ field: `intent.${intent1.name}.examples`,
567
+ label: 'Examples',
568
+ v1Value: `${examples1.length} examples`,
569
+ v2Value: `${examples2.length} examples`,
570
+ type: 'modified'
571
+ });
572
+ }
573
+
574
+ // Compare parameters
575
+ const params1 = intent1.parameters || [];
576
+ const params2 = intent2.parameters || [];
577
+ if (JSON.stringify(params1) !== JSON.stringify(params2)) {
578
+ changes.push({
579
+ field: `intent.${intent1.name}.parameters`,
580
+ label: 'Parameters',
581
+ v1Value: `${params1.length} parameters`,
582
+ v2Value: `${params2.length} parameters`,
583
+ type: 'modified'
584
+ });
585
+ }
586
+
587
+ return changes;
588
+ }
589
+
590
+ getDiffIcon(type: string): string {
591
+ switch (type) {
592
+ case 'added': return 'add_circle';
593
+ case 'removed': return 'remove_circle';
594
+ case 'modified': return 'edit';
595
+ default: return 'circle';
596
+ }
597
+ }
598
+
599
+ formatValue(value: any): string {
600
+ if (value === null || value === undefined) return 'Not set';
601
+ if (typeof value === 'boolean') return value ? 'Yes' : 'No';
602
+ if (typeof value === 'string' && value.length > 100) {
603
+ return value.substring(0, 100) + '...';
604
+ }
605
+ return String(value);
606
+ }
607
+
608
+ close() {
609
+ this.dialogRef.close();
610
+ }
611
+ }