@naniteninja/profile-comparison-lib 1.0.18 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, Optional, Inject, inject, EventEmitter, ViewChild,
|
|
2
|
+
import { InjectionToken, Injectable, Optional, Inject, inject, EventEmitter, ViewChild, Output, Input, Component, NgModule } from '@angular/core';
|
|
3
3
|
import { switchMap as switchMap$1, map as map$1, catchError, tap as tap$1 } from 'rxjs/operators';
|
|
4
4
|
import { from, switchMap, map, of, Observable, BehaviorSubject, throwError, tap, retryWhen, concatMap, timer, forkJoin, catchError as catchError$1 } from 'rxjs';
|
|
5
5
|
import * as i1 from '@angular/common/http';
|
|
6
6
|
import { HttpHeaders, HttpClient, HttpClientModule } from '@angular/common/http';
|
|
7
|
-
import * as
|
|
7
|
+
import * as i2_1 from '@angular/common';
|
|
8
8
|
import { CommonModule } from '@angular/common';
|
|
9
|
-
import * as i3 from '@naniteninja/ionic-lib';
|
|
10
|
-
import { LibMarqueeModule } from '@naniteninja/ionic-lib';
|
|
11
9
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
12
10
|
|
|
13
11
|
/** Backend mode for the profile comparison component. */
|
|
@@ -204,10 +202,10 @@ class CachePersistenceService {
|
|
|
204
202
|
static STORE_OPENAI_EMBEDDINGS = STORE_OPENAI_EMBEDDINGS;
|
|
205
203
|
static STORE_PROFILE_FACE = STORE_PROFILE_FACE;
|
|
206
204
|
static STORE_PROFILE_COMPARE = STORE_PROFILE_COMPARE;
|
|
207
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
208
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
205
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: CachePersistenceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
206
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: CachePersistenceService, providedIn: 'root' });
|
|
209
207
|
}
|
|
210
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
208
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: CachePersistenceService, decorators: [{
|
|
211
209
|
type: Injectable,
|
|
212
210
|
args: [{ providedIn: 'root' }]
|
|
213
211
|
}] });
|
|
@@ -253,10 +251,10 @@ class FileConversionService {
|
|
|
253
251
|
const mime = this.getMimeFromFilename(imageStr);
|
|
254
252
|
return this.urlToFile(imageStr, fileBaseName, mime);
|
|
255
253
|
}
|
|
256
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
257
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
254
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FileConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
255
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FileConversionService, providedIn: 'root' });
|
|
258
256
|
}
|
|
259
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
257
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FileConversionService, decorators: [{
|
|
260
258
|
type: Injectable,
|
|
261
259
|
args: [{
|
|
262
260
|
providedIn: 'root'
|
|
@@ -372,10 +370,10 @@ class ImageCompressionService {
|
|
|
372
370
|
return `${base}.webp`;
|
|
373
371
|
return `${base}.jpg`;
|
|
374
372
|
}
|
|
375
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
376
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
373
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ImageCompressionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
374
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ImageCompressionService, providedIn: 'root' });
|
|
377
375
|
}
|
|
378
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
376
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ImageCompressionService, decorators: [{
|
|
379
377
|
type: Injectable,
|
|
380
378
|
args: [{
|
|
381
379
|
providedIn: 'root'
|
|
@@ -1154,10 +1152,10 @@ class OpenAIEmbeddingService {
|
|
|
1154
1152
|
this.persistence.clear(CachePersistenceService.STORE_OPENAI_SIMILARITY);
|
|
1155
1153
|
this.persistence.clear(CachePersistenceService.STORE_OPENAI_EMBEDDINGS);
|
|
1156
1154
|
}
|
|
1157
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1158
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1155
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: OpenAIEmbeddingService, deps: [{ token: i1.HttpClient }, { token: PROFILE_COMPARISON_API_BASE_URL, optional: true }, { token: CachePersistenceService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1156
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: OpenAIEmbeddingService, providedIn: 'root' });
|
|
1159
1157
|
}
|
|
1160
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1158
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: OpenAIEmbeddingService, decorators: [{
|
|
1161
1159
|
type: Injectable,
|
|
1162
1160
|
args: [{
|
|
1163
1161
|
providedIn: 'root'
|
|
@@ -1189,10 +1187,10 @@ class ProfileComparisonLibService {
|
|
|
1189
1187
|
getVersion() {
|
|
1190
1188
|
return '0.0.0';
|
|
1191
1189
|
}
|
|
1192
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1193
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1190
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1191
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibService, providedIn: 'root' });
|
|
1194
1192
|
}
|
|
1195
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1193
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibService, decorators: [{
|
|
1196
1194
|
type: Injectable,
|
|
1197
1195
|
args: [{
|
|
1198
1196
|
providedIn: 'root'
|
|
@@ -1446,10 +1444,10 @@ class ProfileService {
|
|
|
1446
1444
|
this.compareInterestsCache.clear();
|
|
1447
1445
|
this.persistence.clear(CachePersistenceService.STORE_PROFILE_COMPARE);
|
|
1448
1446
|
}
|
|
1449
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1450
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1447
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileService, deps: [{ token: i1.HttpClient }, { token: PROFILE_COMPARISON_API_BASE_URL, optional: true }, { token: CachePersistenceService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1448
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileService, providedIn: 'root' });
|
|
1451
1449
|
}
|
|
1452
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1450
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileService, decorators: [{
|
|
1453
1451
|
type: Injectable,
|
|
1454
1452
|
args: [{ providedIn: 'root' }]
|
|
1455
1453
|
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
|
|
@@ -1505,10 +1503,10 @@ class ProfileComparisonBackendService {
|
|
|
1505
1503
|
return throwError(() => err);
|
|
1506
1504
|
}));
|
|
1507
1505
|
}
|
|
1508
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1509
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1506
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonBackendService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1507
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonBackendService, providedIn: 'root' });
|
|
1510
1508
|
}
|
|
1511
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1509
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonBackendService, decorators: [{
|
|
1512
1510
|
type: Injectable,
|
|
1513
1511
|
args: [{ providedIn: 'root' }]
|
|
1514
1512
|
}], ctorParameters: () => [] });
|
|
@@ -1520,6 +1518,7 @@ class ProfileComparisonLibComponent {
|
|
|
1520
1518
|
imageCompressionService;
|
|
1521
1519
|
cdr;
|
|
1522
1520
|
getVerboseLogging;
|
|
1521
|
+
static DEFAULT_OBJECT_POSITION = '50% 50%';
|
|
1523
1522
|
static DEFAULT_EYE_ALIGNMENT_BIAS_PX = 50;
|
|
1524
1523
|
static DEFAULT_IMAGE_FORMAT = 'image/jpeg';
|
|
1525
1524
|
static DEFAULT_IMAGE_QUALITY_START = 0.85;
|
|
@@ -1533,7 +1532,6 @@ class ProfileComparisonLibComponent {
|
|
|
1533
1532
|
static DEFAULT_MAX_HEIGHT = 1600;
|
|
1534
1533
|
static SPACER_SMALL = '-';
|
|
1535
1534
|
static SPACER_LARGE = '----';
|
|
1536
|
-
static PLACEHOLDER = '--';
|
|
1537
1535
|
static CONSOLIDATION_SEPARATOR_X = '×';
|
|
1538
1536
|
static CONSOLIDATION_SEPARATOR_SLASH = '/';
|
|
1539
1537
|
static FACEPP_RATE_LIMIT_DELAY_MS = 5000;
|
|
@@ -1541,8 +1539,8 @@ class ProfileComparisonLibComponent {
|
|
|
1541
1539
|
// Drag and shape constants
|
|
1542
1540
|
static SHAPE_BG_HEIGHT = '350px';
|
|
1543
1541
|
static SHAPE_BG_OBJECT_FIT = 'cover';
|
|
1544
|
-
static
|
|
1545
|
-
static
|
|
1542
|
+
static SHAPE_BG1_OBJECT_POSITION = 'right';
|
|
1543
|
+
static SHAPE_BG2_OBJECT_POSITION = 'left';
|
|
1546
1544
|
static DRAG_MIN_WIDTH = 200;
|
|
1547
1545
|
static DRAG_MAX_WIDTH = 400;
|
|
1548
1546
|
static MAX_DRAG_DISTANCE = 100;
|
|
@@ -1558,28 +1556,30 @@ class ProfileComparisonLibComponent {
|
|
|
1558
1556
|
person3Interests: [],
|
|
1559
1557
|
user1Image: '',
|
|
1560
1558
|
user2Image: '',
|
|
1561
|
-
edgeShading: true,
|
|
1562
|
-
shadingColor: 'rgba(44, 40, 47, 0.7)'
|
|
1563
1559
|
};
|
|
1564
1560
|
backendMode = BackendMode.Real;
|
|
1565
1561
|
backendUrl = null;
|
|
1566
1562
|
fadeAllEdges = true;
|
|
1563
|
+
person1Name = 'Josh';
|
|
1564
|
+
person2Name = 'Amy';
|
|
1567
1565
|
matrixDataChange = new EventEmitter();
|
|
1568
1566
|
rawLLMOutputChange = new EventEmitter();
|
|
1569
1567
|
viewProfileClick = new EventEmitter();
|
|
1570
|
-
/** Fires when the user drags a frame to its maximum displacement and
|
|
1571
|
-
* continues pulling past the edge. The parent can use this to swipe to
|
|
1572
|
-
* the next/previous prospect in its own swiper. */
|
|
1573
|
-
edgeDrag = new EventEmitter();
|
|
1574
1568
|
selectedFile = null;
|
|
1575
1569
|
firstImageData = null;
|
|
1576
1570
|
secondImageData = null;
|
|
1577
1571
|
user1Transform = '';
|
|
1578
1572
|
user2Transform = '';
|
|
1573
|
+
user1Top = '0px';
|
|
1574
|
+
user1Height = '100%';
|
|
1575
|
+
user2Top = '0px';
|
|
1576
|
+
user2Height = '100%';
|
|
1579
1577
|
alignmentCalculated = false;
|
|
1580
1578
|
isAligning = false;
|
|
1581
|
-
|
|
1582
|
-
|
|
1579
|
+
/** Set when backend returns an error (e.g. 503 API keys not configured). */
|
|
1580
|
+
backendError = null;
|
|
1581
|
+
user1ObjectPosition = ProfileComparisonLibComponent.DEFAULT_OBJECT_POSITION;
|
|
1582
|
+
user2ObjectPosition = ProfileComparisonLibComponent.DEFAULT_OBJECT_POSITION;
|
|
1583
1583
|
user1NaturalWidth = 0;
|
|
1584
1584
|
user1NaturalHeight = 0;
|
|
1585
1585
|
user2NaturalWidth = 0;
|
|
@@ -1609,34 +1609,6 @@ class ProfileComparisonLibComponent {
|
|
|
1609
1609
|
alignedPerson2Interests = [];
|
|
1610
1610
|
leftProfileClicked = false;
|
|
1611
1611
|
rightProfileClicked = false;
|
|
1612
|
-
leftShapeAnimating = false;
|
|
1613
|
-
rightShapeAnimating = false;
|
|
1614
|
-
similarities = [];
|
|
1615
|
-
// ── Small-screen responsive helpers ──────────────────────────────────────
|
|
1616
|
-
get viewProfileLeftX() {
|
|
1617
|
-
const w = window.innerWidth;
|
|
1618
|
-
if (w <= 360)
|
|
1619
|
-
return 65;
|
|
1620
|
-
if (w <= 400)
|
|
1621
|
-
return 52;
|
|
1622
|
-
if (w <= 600)
|
|
1623
|
-
return 48;
|
|
1624
|
-
return 15;
|
|
1625
|
-
}
|
|
1626
|
-
get viewProfileRightX() {
|
|
1627
|
-
const w = window.innerWidth;
|
|
1628
|
-
if (w <= 360)
|
|
1629
|
-
return 191;
|
|
1630
|
-
if (w <= 400)
|
|
1631
|
-
return 204;
|
|
1632
|
-
if (w <= 600)
|
|
1633
|
-
return 208;
|
|
1634
|
-
return 241;
|
|
1635
|
-
}
|
|
1636
|
-
onResize() {
|
|
1637
|
-
this.cdr.detectChanges();
|
|
1638
|
-
}
|
|
1639
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1640
1612
|
sortedA = [];
|
|
1641
1613
|
sortedB = [];
|
|
1642
1614
|
sortedC = [];
|
|
@@ -1647,8 +1619,6 @@ class ProfileComparisonLibComponent {
|
|
|
1647
1619
|
backendConfigured = false;
|
|
1648
1620
|
/** The resolved backend URL computed from inputs and legacy token. */
|
|
1649
1621
|
resolvedBackendUrl = null;
|
|
1650
|
-
/** Flag to prevent click animations after a drag. */
|
|
1651
|
-
wasDragging = false;
|
|
1652
1622
|
compressionConfig = {
|
|
1653
1623
|
maxWidth: ProfileComparisonLibComponent.DEFAULT_MAX_WIDTH,
|
|
1654
1624
|
maxHeight: ProfileComparisonLibComponent.DEFAULT_MAX_HEIGHT,
|
|
@@ -1666,11 +1636,15 @@ class ProfileComparisonLibComponent {
|
|
|
1666
1636
|
fetchRequestId = 0;
|
|
1667
1637
|
leftContainer;
|
|
1668
1638
|
rightContainer;
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
rightBgFrame;
|
|
1639
|
+
profileFlex;
|
|
1640
|
+
profileImgLeft;
|
|
1641
|
+
profileImgRight;
|
|
1673
1642
|
shapeContainer;
|
|
1643
|
+
shapeBg;
|
|
1644
|
+
shapeBg1;
|
|
1645
|
+
shapeBg2;
|
|
1646
|
+
shapeTextLeft;
|
|
1647
|
+
shapeTextRight;
|
|
1674
1648
|
shapeTextCenter;
|
|
1675
1649
|
constructor(backendService, renderer, fileConversionService, imageCompressionService, cdr, getVerboseLogging) {
|
|
1676
1650
|
this.backendService = backendService;
|
|
@@ -1711,15 +1685,19 @@ class ProfileComparisonLibComponent {
|
|
|
1711
1685
|
this.user1Transform = 'translateY(0px)';
|
|
1712
1686
|
this.user2Transform = 'translateY(0px)';
|
|
1713
1687
|
this.alignmentCalculated = false;
|
|
1688
|
+
this.isAligning = true;
|
|
1689
|
+
this.cdr.detectChanges();
|
|
1714
1690
|
this.log('ngOnInit', 'starting compressConfigImagesIfNeeded then fetchComparison');
|
|
1715
1691
|
this.compressConfigImagesIfNeeded().subscribe({
|
|
1716
1692
|
next: () => {
|
|
1717
1693
|
this.log('ngOnInit', 'compressConfigImagesIfNeeded done, calling fetchComparison');
|
|
1694
|
+
this.isAligning = false;
|
|
1718
1695
|
this.fetchComparison();
|
|
1719
1696
|
},
|
|
1720
1697
|
error: (e) => {
|
|
1721
1698
|
console.warn('Config image compression failed, continuing:', e);
|
|
1722
1699
|
this.log('ngOnInit', 'compressConfigImagesIfNeeded error, calling fetchComparison anyway');
|
|
1700
|
+
this.isAligning = false;
|
|
1723
1701
|
this.fetchComparison();
|
|
1724
1702
|
}
|
|
1725
1703
|
});
|
|
@@ -1733,16 +1711,9 @@ class ProfileComparisonLibComponent {
|
|
|
1733
1711
|
this.backendConfigured = !!this.resolvedBackendUrl;
|
|
1734
1712
|
this.log('ngOnChanges', 'backend inputs changed', 'resolvedUrl', this.resolvedBackendUrl ?? '(null)');
|
|
1735
1713
|
}
|
|
1736
|
-
if (changes['fadeAllEdges']) {
|
|
1737
|
-
this.config.edgeShading = this.fadeAllEdges;
|
|
1738
|
-
}
|
|
1739
1714
|
if (changes['config']) {
|
|
1740
1715
|
const first = changes['config'].firstChange;
|
|
1741
1716
|
this.log('ngOnChanges', 'config changed', 'firstChange', first, 'backendConfigured', this.backendConfigured);
|
|
1742
|
-
// Sync fadeAllEdges if it was explicitly changed via Input but not in config
|
|
1743
|
-
if (this.config.edgeShading === undefined) {
|
|
1744
|
-
this.config.edgeShading = this.fadeAllEdges;
|
|
1745
|
-
}
|
|
1746
1717
|
this.updateConfigProperties();
|
|
1747
1718
|
if (!this.backendConfigured) {
|
|
1748
1719
|
this.log('ngOnChanges', 'skipping fetch: backend not configured');
|
|
@@ -1767,19 +1738,12 @@ class ProfileComparisonLibComponent {
|
|
|
1767
1738
|
}
|
|
1768
1739
|
}
|
|
1769
1740
|
}
|
|
1770
|
-
/**
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
getDashType(index) {
|
|
1777
|
-
const score = this.similarities[index] ?? 0;
|
|
1778
|
-
if (score > 0.8)
|
|
1779
|
-
return 'high';
|
|
1780
|
-
if (score > 0.4)
|
|
1781
|
-
return 'medium';
|
|
1782
|
-
return 'low';
|
|
1741
|
+
/** Use for template: hide left/right item when it's a shared (center) item. Compares case-insensitively. */
|
|
1742
|
+
isInCenter(interest) {
|
|
1743
|
+
if (!interest || !this.centerItem?.length)
|
|
1744
|
+
return false;
|
|
1745
|
+
const n = String(interest).trim().toLowerCase();
|
|
1746
|
+
return this.centerItem.some((c) => String(c).trim().toLowerCase() === n);
|
|
1783
1747
|
}
|
|
1784
1748
|
fetchComparison() {
|
|
1785
1749
|
this.log('fetchComparison', 'start');
|
|
@@ -1793,6 +1757,8 @@ class ProfileComparisonLibComponent {
|
|
|
1793
1757
|
}
|
|
1794
1758
|
const requestId = ++this.fetchRequestId;
|
|
1795
1759
|
this.isAligning = true;
|
|
1760
|
+
this.backendError = null;
|
|
1761
|
+
this.cdr.detectChanges();
|
|
1796
1762
|
const normalized = this.buildConfigWithDefaults(this.config, this.baseConfig);
|
|
1797
1763
|
const configToSend = {
|
|
1798
1764
|
person1Interests: normalized.person1Interests,
|
|
@@ -1815,17 +1781,32 @@ class ProfileComparisonLibComponent {
|
|
|
1815
1781
|
}
|
|
1816
1782
|
this.log('fetchComparison', 'backend response received', { aligned1: payload?.alignedPerson1Interests?.length, aligned2: payload?.alignedPerson2Interests?.length });
|
|
1817
1783
|
this.isAligning = false;
|
|
1818
|
-
|
|
1819
|
-
const a2 = payload.alignedPerson2Interests ?? [];
|
|
1820
|
-
this.alignedPerson1Interests = a1;
|
|
1821
|
-
this.alignedPerson2Interests = a2;
|
|
1822
|
-
this.centerItem = payload.centerItem ?? [];
|
|
1823
|
-
this.similarities = payload.similarities ?? [];
|
|
1824
|
-
this.displayPerson1Interests = this.alignedPerson1Interests;
|
|
1825
|
-
this.displayPerson2Interests = this.alignedPerson2Interests;
|
|
1784
|
+
this.backendError = null;
|
|
1826
1785
|
this.matrixData = payload.matrixData ?? null;
|
|
1827
1786
|
if (this.matrixData)
|
|
1828
1787
|
this.matrixDataChange.emit(this.matrixData);
|
|
1788
|
+
const a1 = payload.alignedPerson1Interests ?? [];
|
|
1789
|
+
const a2 = payload.alignedPerson2Interests ?? [];
|
|
1790
|
+
const sameLists = a1.length === a2.length && a1.every((v, i) => String(v).trim().toLowerCase() === String(a2[i]).trim().toLowerCase());
|
|
1791
|
+
if (sameLists && a1.length > 0) {
|
|
1792
|
+
this.log('fetchComparison', 'backend returned identical aligned lists; using raw lists to keep sides distinct', { len: a1.length });
|
|
1793
|
+
this.alignedPerson1Interests = [];
|
|
1794
|
+
this.alignedPerson2Interests = [];
|
|
1795
|
+
this.displayPerson1Interests = this.person1Interests;
|
|
1796
|
+
this.displayPerson2Interests = this.person2Interests;
|
|
1797
|
+
this.centerItem = payload.centerItem ?? [];
|
|
1798
|
+
}
|
|
1799
|
+
else {
|
|
1800
|
+
const rawCenter = payload.centerItem ?? [];
|
|
1801
|
+
const list1ToUse = this.person1Interests?.length ? this.person1Interests : a1;
|
|
1802
|
+
const list2ToUse = this.person2Interests?.length ? this.person2Interests : a2;
|
|
1803
|
+
const reordered = this.reorderInterestsByMatrix(list1ToUse, list2ToUse, rawCenter);
|
|
1804
|
+
this.alignedPerson1Interests = reordered.p1;
|
|
1805
|
+
this.alignedPerson2Interests = reordered.p2;
|
|
1806
|
+
this.displayPerson1Interests = reordered.p1;
|
|
1807
|
+
this.displayPerson2Interests = reordered.p2;
|
|
1808
|
+
this.centerItem = reordered.center;
|
|
1809
|
+
}
|
|
1829
1810
|
if (payload.rawLLMOutput != null) {
|
|
1830
1811
|
this.rawLLMOutput = payload.rawLLMOutput;
|
|
1831
1812
|
this.rawLLMOutputChange.emit(payload.rawLLMOutput);
|
|
@@ -1851,6 +1832,7 @@ class ProfileComparisonLibComponent {
|
|
|
1851
1832
|
}
|
|
1852
1833
|
this.log('fetchComparison', 'backend error', err);
|
|
1853
1834
|
const msg = err?.error?.message ?? err?.message ?? err?.statusText ?? (typeof err?.error === 'string' ? err.error : null) ?? 'Request failed';
|
|
1835
|
+
this.backendError = msg;
|
|
1854
1836
|
console.error('[ProfileComparisonLib] fetchComparison error', err?.status ?? '', msg);
|
|
1855
1837
|
this.isAligning = false;
|
|
1856
1838
|
this.cdr.detectChanges();
|
|
@@ -1864,6 +1846,10 @@ class ProfileComparisonLibComponent {
|
|
|
1864
1846
|
this.person2Interests = [...normalized.person2Interests];
|
|
1865
1847
|
this.user1Image = normalized.user1Image;
|
|
1866
1848
|
this.user2Image = normalized.user2Image;
|
|
1849
|
+
if (normalized.person1Name)
|
|
1850
|
+
this.person1Name = normalized.person1Name;
|
|
1851
|
+
if (normalized.person2Name)
|
|
1852
|
+
this.person2Name = normalized.person2Name;
|
|
1867
1853
|
}
|
|
1868
1854
|
buildConfigWithDefaults(cfg, base) {
|
|
1869
1855
|
const list = (val, fb) => {
|
|
@@ -1882,13 +1868,13 @@ class ProfileComparisonLibComponent {
|
|
|
1882
1868
|
};
|
|
1883
1869
|
const safe = cfg || {};
|
|
1884
1870
|
return {
|
|
1871
|
+
person1Name: safe.person1Name ? str(safe.person1Name, '') : undefined,
|
|
1872
|
+
person2Name: safe.person2Name ? str(safe.person2Name, '') : undefined,
|
|
1885
1873
|
person1Interests: list(safe.person1Interests, base.person1Interests),
|
|
1886
1874
|
person2Interests: list(safe.person2Interests, base.person2Interests),
|
|
1887
1875
|
person3Interests: list(safe.person3Interests, base.person3Interests),
|
|
1888
1876
|
user1Image: str(safe.user1Image, base.user1Image),
|
|
1889
1877
|
user2Image: str(safe.user2Image, base.user2Image),
|
|
1890
|
-
edgeShading: safe.edgeShading ?? base.edgeShading ?? true,
|
|
1891
|
-
shadingColor: safe.shadingColor ?? base.shadingColor ?? 'rgba(44, 40, 47, 0.7)',
|
|
1892
1878
|
};
|
|
1893
1879
|
}
|
|
1894
1880
|
onUserImageLoad(which, event) {
|
|
@@ -1918,9 +1904,9 @@ class ProfileComparisonLibComponent {
|
|
|
1918
1904
|
const natH = which === 1 ? this.user1NaturalHeight : this.user2NaturalHeight;
|
|
1919
1905
|
if (!face || !natW || !natH) {
|
|
1920
1906
|
if (which === 1)
|
|
1921
|
-
this.user1ObjectPosition = ProfileComparisonLibComponent.
|
|
1907
|
+
this.user1ObjectPosition = ProfileComparisonLibComponent.DEFAULT_OBJECT_POSITION;
|
|
1922
1908
|
else
|
|
1923
|
-
this.user2ObjectPosition = ProfileComparisonLibComponent.
|
|
1909
|
+
this.user2ObjectPosition = ProfileComparisonLibComponent.DEFAULT_OBJECT_POSITION;
|
|
1924
1910
|
return;
|
|
1925
1911
|
}
|
|
1926
1912
|
const eyes = this.getEyeCoordinatesFromFace(face);
|
|
@@ -2016,7 +2002,7 @@ class ProfileComparisonLibComponent {
|
|
|
2016
2002
|
}
|
|
2017
2003
|
getProfileContainerSize(which) {
|
|
2018
2004
|
try {
|
|
2019
|
-
const el = (which === 1 ? this.
|
|
2005
|
+
const el = (which === 1 ? this.profileImgLeft : this.profileImgRight)?.nativeElement || null;
|
|
2020
2006
|
if (!el)
|
|
2021
2007
|
return { width: 0, height: 0 };
|
|
2022
2008
|
const rect = el.getBoundingClientRect();
|
|
@@ -2027,384 +2013,297 @@ class ProfileComparisonLibComponent {
|
|
|
2027
2013
|
}
|
|
2028
2014
|
}
|
|
2029
2015
|
getOverlapSizeCssPx() {
|
|
2030
|
-
|
|
2016
|
+
try {
|
|
2017
|
+
const root = this.profileFlex?.nativeElement || null;
|
|
2018
|
+
const fallback = 40;
|
|
2019
|
+
if (!root)
|
|
2020
|
+
return fallback;
|
|
2021
|
+
const val = getComputedStyle(root).getPropertyValue('--overlap-size').trim();
|
|
2022
|
+
const parsed = parseFloat(val);
|
|
2023
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
2024
|
+
}
|
|
2025
|
+
catch {
|
|
2026
|
+
return 40;
|
|
2027
|
+
}
|
|
2031
2028
|
}
|
|
2032
2029
|
setOverlapSizeCssPx(px) {
|
|
2033
|
-
|
|
2030
|
+
try {
|
|
2031
|
+
const root = this.profileFlex?.nativeElement || null;
|
|
2032
|
+
if (!root)
|
|
2033
|
+
return;
|
|
2034
|
+
const clamped = Math.max(8, Math.min(80, Math.round(px)));
|
|
2035
|
+
root.style.setProperty('--overlap-size', `${clamped}px`);
|
|
2036
|
+
}
|
|
2037
|
+
catch { }
|
|
2034
2038
|
}
|
|
2035
2039
|
waitForImagesAndInitDrag() {
|
|
2036
|
-
const
|
|
2037
|
-
const
|
|
2038
|
-
if (!
|
|
2039
|
-
setTimeout(() => this.waitForImagesAndInitDrag(), 100);
|
|
2040
|
+
const shapeBg1 = this.shapeBg1?.nativeElement;
|
|
2041
|
+
const shapeBg2 = this.shapeBg2?.nativeElement;
|
|
2042
|
+
if (!shapeBg1 || !shapeBg2) {
|
|
2040
2043
|
return;
|
|
2041
2044
|
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
const container = this.shapeContainer?.nativeElement;
|
|
2055
|
-
if (!container)
|
|
2056
|
-
return;
|
|
2057
|
-
const containerRect = container.getBoundingClientRect();
|
|
2058
|
-
const frames = [
|
|
2059
|
-
{ side: 'left', el: this.leftFrame?.nativeElement, bg: this.leftBgFrame?.nativeElement },
|
|
2060
|
-
{ side: 'right', el: this.rightFrame?.nativeElement, bg: this.rightBgFrame?.nativeElement }
|
|
2061
|
-
];
|
|
2062
|
-
frames.forEach(f => {
|
|
2063
|
-
if (!f.el)
|
|
2064
|
-
return;
|
|
2065
|
-
const rect = f.el.getBoundingClientRect();
|
|
2066
|
-
const className = `edge-at-${f.side}-border`;
|
|
2067
|
-
const touches = f.side === 'left'
|
|
2068
|
-
? rect.left <= containerRect.left + 1
|
|
2069
|
-
: rect.right >= containerRect.right - 1;
|
|
2070
|
-
f.el.classList.toggle(className, touches);
|
|
2071
|
-
if (f.bg)
|
|
2072
|
-
f.bg.classList.toggle(className, touches);
|
|
2073
|
-
});
|
|
2045
|
+
const checkImagesLoaded = () => {
|
|
2046
|
+
// For SVG elements, check if they're rendered instead of naturalWidth
|
|
2047
|
+
const img1Loaded = shapeBg1 && shapeBg1.getBoundingClientRect().width > 0;
|
|
2048
|
+
const img2Loaded = shapeBg2 && shapeBg2.getBoundingClientRect().width > 0;
|
|
2049
|
+
if (img1Loaded && img2Loaded) {
|
|
2050
|
+
setTimeout(() => this.initDrag(), 50);
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
setTimeout(checkImagesLoaded, 100);
|
|
2054
|
+
}
|
|
2055
|
+
};
|
|
2056
|
+
checkImagesLoaded();
|
|
2074
2057
|
}
|
|
2075
2058
|
initDrag() {
|
|
2076
|
-
const
|
|
2077
|
-
const
|
|
2078
|
-
const
|
|
2079
|
-
const
|
|
2080
|
-
|
|
2081
|
-
if (!leftFrame || !rightFrame)
|
|
2059
|
+
const shapeBg1 = this.shapeBg1?.nativeElement;
|
|
2060
|
+
const shapeBg2 = this.shapeBg2?.nativeElement;
|
|
2061
|
+
const shapeTextLeft = this.shapeTextLeft?.nativeElement || null;
|
|
2062
|
+
const shapeTextRight = this.shapeTextRight?.nativeElement || null;
|
|
2063
|
+
if (!shapeBg1 || !shapeBg2 || !shapeTextLeft || !shapeTextRight)
|
|
2082
2064
|
return;
|
|
2083
|
-
//
|
|
2084
|
-
const
|
|
2085
|
-
const
|
|
2065
|
+
// Cast to HTMLElement to access style properties conveniently, as SVGElement style handling can be similar in this context
|
|
2066
|
+
const shapeBg1El = shapeBg1;
|
|
2067
|
+
const shapeBg2El = shapeBg2;
|
|
2068
|
+
shapeBg1El.style.height = ProfileComparisonLibComponent.SHAPE_BG_HEIGHT;
|
|
2069
|
+
// shapeBg1El.style.objectFit = ProfileComparisonLibComponent.SHAPE_BG_OBJECT_FIT;
|
|
2070
|
+
// shapeBg1El.style.objectPosition = ProfileComparisonLibComponent.SHAPE_BG1_OBJECT_POSITION;
|
|
2071
|
+
shapeBg2El.style.height = ProfileComparisonLibComponent.SHAPE_BG_HEIGHT;
|
|
2072
|
+
// shapeBg2El.style.objectFit = ProfileComparisonLibComponent.SHAPE_BG_OBJECT_FIT;
|
|
2073
|
+
// shapeBg2El.style.objectPosition = ProfileComparisonLibComponent.SHAPE_BG2_OBJECT_POSITION;
|
|
2074
|
+
const originalWidth1 = shapeBg1.getBoundingClientRect().width;
|
|
2075
|
+
const originalWidth2 = shapeBg2.getBoundingClientRect().width;
|
|
2076
|
+
const minWidth = ProfileComparisonLibComponent.DRAG_MIN_WIDTH;
|
|
2077
|
+
const maxWidth = ProfileComparisonLibComponent.DRAG_MAX_WIDTH;
|
|
2086
2078
|
let isDragging = false;
|
|
2087
2079
|
let dragStartX = 0;
|
|
2088
|
-
let
|
|
2089
|
-
let
|
|
2090
|
-
|
|
2091
|
-
|
|
2080
|
+
let initialWidth1 = 0;
|
|
2081
|
+
let initialWidth2 = 0;
|
|
2082
|
+
let activeImg = null;
|
|
2083
|
+
// Use ViewChild for center text
|
|
2084
|
+
const shapeTextCenter = this.shapeTextCenter?.nativeElement || null;
|
|
2092
2085
|
const onMouseMove = (e) => {
|
|
2093
|
-
if (!isDragging)
|
|
2086
|
+
if (!isDragging || !activeImg)
|
|
2094
2087
|
return;
|
|
2095
2088
|
const deltaX = e.clientX - dragStartX;
|
|
2096
|
-
const containerWidth =
|
|
2097
|
-
|
|
2098
|
-
let
|
|
2099
|
-
if (
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
// Check for edge drag swiping (both mouse and touch support!)
|
|
2103
|
-
const leftRect = leftFrame.getBoundingClientRect();
|
|
2104
|
-
const rightRect = rightFrame.getBoundingClientRect();
|
|
2105
|
-
const overlapPx = Math.max(0, leftRect.right - leftRect.left); // Frame width
|
|
2106
|
-
const maxDisplacementPx = overlapPx / 3;
|
|
2107
|
-
if (!edgeDragEmitted && Math.abs(deltaX) > maxDisplacementPx * 1.05) {
|
|
2108
|
-
// Only trigger swipe outward:
|
|
2109
|
-
// Left frame dragged further left (deltaX < 0) or Right frame dragged further right (deltaX > 0)
|
|
2110
|
-
if ((activeFrame === leftFrame && deltaX < 0) || (activeFrame === rightFrame && deltaX > 0)) {
|
|
2111
|
-
edgeDragEmitted = true;
|
|
2112
|
-
this.edgeDrag.emit(deltaX < 0 ? 'left' : 'right');
|
|
2113
|
-
}
|
|
2089
|
+
const containerWidth = this.shapeContainer?.nativeElement?.clientWidth || 0;
|
|
2090
|
+
let newWidth1 = initialWidth1;
|
|
2091
|
+
let newWidth2 = initialWidth2;
|
|
2092
|
+
if (activeImg === shapeBg1) {
|
|
2093
|
+
newWidth1 = initialWidth1 + deltaX;
|
|
2094
|
+
newWidth2 = initialWidth2 - deltaX;
|
|
2114
2095
|
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
// Frame width is 75% of container width
|
|
2119
|
-
const frameMovePx = (deltaPercent / 100) * (containerWidth * 0.75);
|
|
2120
|
-
let newLeftTranslate = initialTranslateXLeft;
|
|
2121
|
-
let newRightTranslate = initialTranslateXRight;
|
|
2122
|
-
// Staggered text movement references
|
|
2123
|
-
const leftContent = leftFrame.querySelector('.profile-comparison__content--left');
|
|
2124
|
-
const rightContent = rightFrame.querySelector('.profile-comparison__content--right');
|
|
2125
|
-
// Mouse-controlled sequential staged text movement (no auto-animations)
|
|
2126
|
-
const maxStageMove = Math.abs(initialMaxDisplacementPercent * 0.75 * containerWidth / 100);
|
|
2127
|
-
const getStagedX = (current, startPct, endPct, totalDisplacementPx) => {
|
|
2128
|
-
const absD = Math.abs(current);
|
|
2129
|
-
const startPx = maxStageMove * startPct;
|
|
2130
|
-
const endPx = maxStageMove * endPct;
|
|
2131
|
-
if (absD <= startPx)
|
|
2132
|
-
return 0;
|
|
2133
|
-
// Linear progress [0, 1]
|
|
2134
|
-
let progress = Math.min(1, (absD - startPx) / (endPx - startPx));
|
|
2135
|
-
// Apply smoothstep easing (3x^2 - 2x^3) for soft start/end
|
|
2136
|
-
progress = progress * progress * (3 - 2 * progress);
|
|
2137
|
-
return progress * totalDisplacementPx * Math.sign(current);
|
|
2138
|
-
};
|
|
2139
|
-
const leadTarget = 35;
|
|
2140
|
-
const followTarget = 40;
|
|
2141
|
-
const centerTarget = 15;
|
|
2142
|
-
// Use a moderate transition to smooth out mouse jitter while maintaining control
|
|
2143
|
-
const textSmoothing = 'transform 0.4s cubic-bezier(0.2, 0, 0.2, 1)';
|
|
2144
|
-
if (leftContent)
|
|
2145
|
-
leftContent.style.transition = textSmoothing;
|
|
2146
|
-
if (rightContent)
|
|
2147
|
-
rightContent.style.transition = textSmoothing;
|
|
2148
|
-
if (shapeTextCenter)
|
|
2149
|
-
shapeTextCenter.style.transition = textSmoothing;
|
|
2150
|
-
if (deltaPercent < 0) {
|
|
2151
|
-
// Dragging left: only left shape moves further left
|
|
2152
|
-
newLeftTranslate = initialTranslateXLeft + deltaPercent;
|
|
2153
|
-
leftFrame.style.transform = `translateX(${newLeftTranslate}%) scale(0.86)`;
|
|
2154
|
-
if (leftBgFrame) {
|
|
2155
|
-
leftBgFrame.style.transform = `translateX(${newLeftTranslate}%) scale(0.86)`;
|
|
2156
|
-
const bg = leftBgFrame.querySelector('.profile-comparison__bg');
|
|
2157
|
-
if (bg) {
|
|
2158
|
-
bg.style.position = 'absolute';
|
|
2159
|
-
bg.style.left = '0%';
|
|
2160
|
-
bg.style.right = `${deltaPercent}%`; // Expand right when dragging left
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
// Ensure right frame is at rest
|
|
2164
|
-
rightFrame.style.transform = `translateX(${initialTranslateXRight}%) scale(0.86)`;
|
|
2165
|
-
if (rightBgFrame) {
|
|
2166
|
-
rightBgFrame.style.transform = `translateX(${initialTranslateXRight}%) scale(0.86)`;
|
|
2167
|
-
const bg = rightBgFrame.querySelector('.profile-comparison__bg');
|
|
2168
|
-
if (bg) {
|
|
2169
|
-
bg.style.position = 'absolute';
|
|
2170
|
-
bg.style.left = '0%';
|
|
2171
|
-
bg.style.right = '0%';
|
|
2172
|
-
bg.style.backgroundPositionX = ''; // restore CSS default (right)
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
// Lead = Left, Follow = Right
|
|
2176
|
-
if (leftContent)
|
|
2177
|
-
leftContent.style.transform = `translateX(${getStagedX(frameMovePx, 0, 0.4, -leadTarget)}px)`;
|
|
2178
|
-
if (rightContent)
|
|
2179
|
-
rightContent.style.transform = `translateX(${getStagedX(frameMovePx, 0.4, 0.8, followTarget)}px)`;
|
|
2180
|
-
if (shapeTextCenter)
|
|
2181
|
-
shapeTextCenter.style.transform = `translateX(${getStagedX(frameMovePx, 0.8, 1.0, centerTarget)}px) scale(0.86)`;
|
|
2096
|
+
else if (activeImg === shapeBg2) {
|
|
2097
|
+
newWidth1 = initialWidth1 - deltaX;
|
|
2098
|
+
newWidth2 = initialWidth2 + deltaX;
|
|
2182
2099
|
}
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
}
|
|
2199
|
-
// Ensure left frame is at rest
|
|
2200
|
-
leftFrame.style.transform = `translateX(${initialTranslateXLeft}%) scale(0.86)`;
|
|
2201
|
-
if (leftBgFrame) {
|
|
2202
|
-
leftBgFrame.style.transform = `translateX(${initialTranslateXLeft}%) scale(0.86)`;
|
|
2203
|
-
const bg = leftBgFrame.querySelector('.profile-comparison__bg');
|
|
2204
|
-
if (bg) {
|
|
2205
|
-
bg.style.position = 'absolute';
|
|
2206
|
-
bg.style.left = '0%';
|
|
2207
|
-
bg.style.right = '0%';
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
// Lead = Right, Follow = Left
|
|
2211
|
-
if (rightContent)
|
|
2212
|
-
rightContent.style.transform = `translateX(${getStagedX(frameMovePx, 0, 0.4, -leadTarget)}px)`;
|
|
2213
|
-
if (leftContent)
|
|
2214
|
-
leftContent.style.transform = `translateX(${getStagedX(frameMovePx, 0.4, 0.8, followTarget)}px)`;
|
|
2215
|
-
if (shapeTextCenter)
|
|
2216
|
-
shapeTextCenter.style.transform = `translateX(${getStagedX(frameMovePx, 0.8, 1.0, centerTarget)}px) scale(0.86)`;
|
|
2100
|
+
newWidth1 = Math.max(minWidth, Math.min(maxWidth, newWidth1));
|
|
2101
|
+
newWidth2 = Math.max(minWidth, Math.min(maxWidth, newWidth2));
|
|
2102
|
+
shapeBg1El.style.width = newWidth1 + 'px';
|
|
2103
|
+
shapeBg1El.style.height = ProfileComparisonLibComponent.SHAPE_BG_HEIGHT;
|
|
2104
|
+
shapeBg2El.style.width = newWidth2 + 'px';
|
|
2105
|
+
shapeBg2El.style.height = ProfileComparisonLibComponent.SHAPE_BG_HEIGHT;
|
|
2106
|
+
const widthChange1 = newWidth1 - originalWidth1;
|
|
2107
|
+
const widthChange2 = newWidth2 - originalWidth2;
|
|
2108
|
+
const dragDirection = deltaX > 0 ? 1 : -1;
|
|
2109
|
+
const dragDistance = Math.abs(deltaX);
|
|
2110
|
+
const maxDragDistance = ProfileComparisonLibComponent.MAX_DRAG_DISTANCE;
|
|
2111
|
+
const centerMoveDistance = Math.min(dragDistance, maxDragDistance) * dragDirection * ProfileComparisonLibComponent.CENTER_MOVE_MULTIPLIER;
|
|
2112
|
+
shapeTextLeft.style.transform = `translateX(${widthChange1 * ProfileComparisonLibComponent.TEXT_MOVE_MULTIPLIER}px)`;
|
|
2113
|
+
shapeTextRight.style.transform = `translateX(${-widthChange2 * ProfileComparisonLibComponent.TEXT_MOVE_MULTIPLIER}px)`;
|
|
2114
|
+
if (shapeTextCenter) {
|
|
2115
|
+
shapeTextCenter.style.transform = `translateX(${centerMoveDistance * ProfileComparisonLibComponent.TEXT_MOVE_MULTIPLIER}px)`;
|
|
2217
2116
|
}
|
|
2218
|
-
// Update edge masks during drag
|
|
2219
|
-
this.updateEdgeMasks();
|
|
2220
2117
|
};
|
|
2221
|
-
const
|
|
2118
|
+
const resetWidths = () => {
|
|
2222
2119
|
isDragging = false;
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
gl.style.transition = 'none';
|
|
2239
|
-
gl.style.opacity = '0.03';
|
|
2240
|
-
}
|
|
2241
|
-
});
|
|
2242
|
-
// Use requestAnimationFrame to ensure transition is applied before position reset
|
|
2243
|
-
requestAnimationFrame(() => {
|
|
2244
|
-
leftFrame.style.transform = `translateX(-24%) scale(0.86)`;
|
|
2245
|
-
rightFrame.style.transform = `translateX(24%) scale(0.86)`;
|
|
2246
|
-
if (leftBgFrame) {
|
|
2247
|
-
leftBgFrame.style.transform = `translateX(-24%) scale(0.86)`;
|
|
2248
|
-
const bg = leftBgFrame.querySelector('.profile-comparison__bg');
|
|
2249
|
-
if (bg) {
|
|
2250
|
-
bg.style.transition = `left 0.6s cubic-bezier(0.16, 1, 0.3, 1), right 0.6s cubic-bezier(0.16, 1, 0.3, 1), ${reboundTransition}`;
|
|
2251
|
-
bg.style.left = '0%';
|
|
2252
|
-
bg.style.right = '0%';
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
if (rightBgFrame) {
|
|
2256
|
-
rightBgFrame.style.transform = `translateX(24%) scale(0.86)`;
|
|
2257
|
-
const bg = rightBgFrame.querySelector('.profile-comparison__bg');
|
|
2258
|
-
if (bg) {
|
|
2259
|
-
bg.style.transition = `left 0.6s cubic-bezier(0.16, 1, 0.3, 1), right 0.6s cubic-bezier(0.16, 1, 0.3, 1), ${reboundTransition}`;
|
|
2260
|
-
bg.style.left = '0%';
|
|
2261
|
-
bg.style.right = '0%';
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
2264
|
-
// Force reset all transforms and clear inline styles
|
|
2265
|
-
[leftContent, rightContent].forEach(el => {
|
|
2266
|
-
if (el) {
|
|
2267
|
-
el.style.transform = 'translateX(0px)';
|
|
2268
|
-
el.style.transition = reboundTransition;
|
|
2269
|
-
}
|
|
2270
|
-
});
|
|
2271
|
-
if (shapeTextCenter) {
|
|
2272
|
-
shapeTextCenter.style.transform = 'translateX(0px) scale(0.86)';
|
|
2273
|
-
shapeTextCenter.style.transition = reboundTransition;
|
|
2274
|
-
}
|
|
2275
|
-
// Restore glass shadow opacity with spring rebound
|
|
2276
|
-
[leftGlass, rightGlass].forEach(gl => {
|
|
2277
|
-
if (gl) {
|
|
2278
|
-
gl.style.transition = 'opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1)';
|
|
2279
|
-
gl.style.opacity = '1';
|
|
2280
|
-
}
|
|
2281
|
-
});
|
|
2282
|
-
});
|
|
2120
|
+
activeImg = null;
|
|
2121
|
+
shapeBg1El.style.transition = ProfileComparisonLibComponent.TRANSITION_WIDTH;
|
|
2122
|
+
shapeBg2El.style.transition = ProfileComparisonLibComponent.TRANSITION_WIDTH;
|
|
2123
|
+
shapeTextLeft.style.transition = ProfileComparisonLibComponent.TRANSITION_TRANSFORM;
|
|
2124
|
+
shapeTextRight.style.transition = ProfileComparisonLibComponent.TRANSITION_TRANSFORM;
|
|
2125
|
+
if (shapeTextCenter) {
|
|
2126
|
+
shapeTextCenter.style.transition = ProfileComparisonLibComponent.TRANSITION_TRANSFORM;
|
|
2127
|
+
}
|
|
2128
|
+
shapeBg1El.style.width = originalWidth1 + 'px';
|
|
2129
|
+
shapeBg2El.style.width = originalWidth2 + 'px';
|
|
2130
|
+
shapeTextLeft.style.transform = ProfileComparisonLibComponent.TRANSFORM_RESET;
|
|
2131
|
+
shapeTextRight.style.transform = ProfileComparisonLibComponent.TRANSFORM_RESET;
|
|
2132
|
+
if (shapeTextCenter) {
|
|
2133
|
+
shapeTextCenter.style.transform = ProfileComparisonLibComponent.TRANSFORM_RESET;
|
|
2134
|
+
}
|
|
2283
2135
|
document.removeEventListener('mousemove', onMouseMove);
|
|
2284
|
-
document.removeEventListener('mouseup',
|
|
2285
|
-
document.removeEventListener('mouseleave',
|
|
2286
|
-
// Re-evaluate edge masks after rebound animation completes
|
|
2287
|
-
setTimeout(() => this.updateEdgeMasks(), 650);
|
|
2136
|
+
document.removeEventListener('mouseup', resetWidths);
|
|
2137
|
+
document.removeEventListener('mouseleave', resetWidths);
|
|
2288
2138
|
setTimeout(() => {
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
bg.style.left = '';
|
|
2298
|
-
bg.style.right = '';
|
|
2299
|
-
bg.style.backgroundPositionX = '';
|
|
2300
|
-
}
|
|
2301
|
-
}
|
|
2302
|
-
});
|
|
2303
|
-
// Clear glass opacity inline style after transition completes
|
|
2304
|
-
[leftGlass, rightGlass].forEach(gl => {
|
|
2305
|
-
if (gl) {
|
|
2306
|
-
gl.style.transition = '';
|
|
2307
|
-
gl.style.opacity = '';
|
|
2308
|
-
}
|
|
2309
|
-
});
|
|
2310
|
-
}, 600);
|
|
2139
|
+
shapeBg1El.style.transition = '';
|
|
2140
|
+
shapeBg2El.style.transition = '';
|
|
2141
|
+
shapeTextLeft.style.transition = '';
|
|
2142
|
+
shapeTextRight.style.transition = '';
|
|
2143
|
+
if (shapeTextCenter) {
|
|
2144
|
+
shapeTextCenter.style.transition = '';
|
|
2145
|
+
}
|
|
2146
|
+
}, ProfileComparisonLibComponent.TRANSITION_RESET_DELAY_MS);
|
|
2311
2147
|
};
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
// Ensure cursor visually indicates grab on the svg images inside the frame
|
|
2315
|
-
const svgs = frame.querySelectorAll('svg');
|
|
2316
|
-
svgs.forEach(s => s.style.cursor = 'grab');
|
|
2317
|
-
frame.addEventListener('mousedown', (e) => {
|
|
2318
|
-
// Prevent default text selection during drag
|
|
2148
|
+
[shapeBg1, shapeBg2].forEach((img) => {
|
|
2149
|
+
img.addEventListener('mousedown', (e) => {
|
|
2319
2150
|
e.preventDefault();
|
|
2320
2151
|
isDragging = true;
|
|
2321
|
-
|
|
2322
|
-
activeFrame = frame; // Track which frame is active
|
|
2152
|
+
activeImg = img;
|
|
2323
2153
|
dragStartX = e.clientX;
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
if (activeGlass) {
|
|
2327
|
-
// Instant drop on grab
|
|
2328
|
-
activeGlass.style.transition = 'none';
|
|
2329
|
-
activeGlass.style.opacity = '0.15';
|
|
2330
|
-
// Next frame: begin slow return to normal while user holds
|
|
2331
|
-
requestAnimationFrame(() => {
|
|
2332
|
-
if (activeGlass) {
|
|
2333
|
-
activeGlass.style.transition = 'opacity 1.5s ease-in';
|
|
2334
|
-
activeGlass.style.opacity = '1';
|
|
2335
|
-
}
|
|
2336
|
-
});
|
|
2337
|
-
}
|
|
2338
|
-
// Calculate initial max displacement once at the start of drag
|
|
2339
|
-
const containerWidth = leftFrame.parentElement?.clientWidth || 360;
|
|
2340
|
-
const leftRect = leftFrame.getBoundingClientRect();
|
|
2341
|
-
const rightRect = rightFrame.getBoundingClientRect();
|
|
2342
|
-
const overlapPx = Math.max(0, leftRect.right - rightRect.left);
|
|
2343
|
-
const maxDisplacementPx = overlapPx / 3;
|
|
2344
|
-
initialMaxDisplacementPercent = (maxDisplacementPx / containerWidth) * 100;
|
|
2154
|
+
initialWidth1 = shapeBg1.getBoundingClientRect().width;
|
|
2155
|
+
initialWidth2 = shapeBg2.getBoundingClientRect().width;
|
|
2345
2156
|
document.addEventListener('mousemove', onMouseMove);
|
|
2346
|
-
document.addEventListener('mouseup',
|
|
2347
|
-
document.addEventListener('mouseleave',
|
|
2157
|
+
document.addEventListener('mouseup', resetWidths);
|
|
2158
|
+
document.addEventListener('mouseleave', resetWidths);
|
|
2348
2159
|
});
|
|
2349
|
-
frame.addEventListener('touchstart', (e) => {
|
|
2350
|
-
// Prevent default to avoid scrolling/swiping
|
|
2351
|
-
if (e.cancelable)
|
|
2352
|
-
e.preventDefault();
|
|
2353
|
-
// Stop propagation to prevent swiper from seeing this touch
|
|
2354
|
-
e.stopPropagation();
|
|
2355
|
-
isDragging = true;
|
|
2356
|
-
this.wasDragging = false;
|
|
2357
|
-
activeFrame = frame; // Track which frame is active
|
|
2358
|
-
dragStartX = e.touches[0].clientX;
|
|
2359
|
-
const activeGlass = frame === leftFrame ? leftGlass : rightGlass;
|
|
2360
|
-
if (activeGlass) {
|
|
2361
|
-
activeGlass.style.transition = 'none';
|
|
2362
|
-
activeGlass.style.opacity = '0.15';
|
|
2363
|
-
requestAnimationFrame(() => {
|
|
2364
|
-
if (activeGlass) {
|
|
2365
|
-
activeGlass.style.transition = 'opacity 1.5s ease-in';
|
|
2366
|
-
activeGlass.style.opacity = '1';
|
|
2367
|
-
}
|
|
2368
|
-
});
|
|
2369
|
-
}
|
|
2370
|
-
const onTouchMove = (ev) => {
|
|
2371
|
-
onMouseMove({ clientX: ev.touches[0].clientX });
|
|
2372
|
-
};
|
|
2373
|
-
const onTouchEnd = () => {
|
|
2374
|
-
resetDrag();
|
|
2375
|
-
document.removeEventListener('touchmove', onTouchMove);
|
|
2376
|
-
document.removeEventListener('touchend', onTouchEnd);
|
|
2377
|
-
};
|
|
2378
|
-
document.addEventListener('touchmove', onTouchMove, { passive: false });
|
|
2379
|
-
document.addEventListener('touchend', onTouchEnd);
|
|
2380
|
-
document.addEventListener('touchcancel', onTouchEnd);
|
|
2381
|
-
}, { passive: false });
|
|
2382
2160
|
});
|
|
2383
2161
|
}
|
|
2384
|
-
|
|
2385
|
-
if (
|
|
2386
|
-
|
|
2387
|
-
|
|
2162
|
+
getSimilarity(item1, item2) {
|
|
2163
|
+
if (!this.matrixData || !item1 || !item2)
|
|
2164
|
+
return 0;
|
|
2165
|
+
const n1 = String(item1).trim().toLowerCase();
|
|
2166
|
+
const n2 = String(item2).trim().toLowerCase();
|
|
2167
|
+
if (n1 === n2)
|
|
2168
|
+
return 1.0;
|
|
2169
|
+
const leg1 = this.matrixData.legend?.find(l => l.full.trim().toLowerCase() === n1);
|
|
2170
|
+
const leg2 = this.matrixData.legend?.find(l => l.full.trim().toLowerCase() === n2);
|
|
2171
|
+
if (!leg1 || !leg2)
|
|
2172
|
+
return 0;
|
|
2173
|
+
const colIdx = this.matrixData.headers?.indexOf(leg2.abbr);
|
|
2174
|
+
if (colIdx === -1 || colIdx === undefined)
|
|
2175
|
+
return 0;
|
|
2176
|
+
const row = this.matrixData.rows?.find(r => r.label === leg1.abbr);
|
|
2177
|
+
if (!row || !row.values || colIdx >= row.values.length)
|
|
2178
|
+
return 0;
|
|
2179
|
+
const val = parseFloat(row.values[colIdx]);
|
|
2180
|
+
return isNaN(val) ? 0 : val;
|
|
2181
|
+
}
|
|
2182
|
+
reorderInterestsByMatrix(p1List, p2List, centerList) {
|
|
2183
|
+
if (!this.matrixData)
|
|
2184
|
+
return { p1: p1List, p2: p2List, center: centerList };
|
|
2185
|
+
const cleanCenter = (centerList || []).filter(c => c && c !== '-' && c !== '----');
|
|
2186
|
+
const list1 = (p1List || []).filter(item => item && item !== '-' && item !== '----');
|
|
2187
|
+
const list2 = (p2List || []).filter(item => item && item !== '-' && item !== '----');
|
|
2188
|
+
const ordered1 = [];
|
|
2189
|
+
const ordered2 = [];
|
|
2190
|
+
const used1 = new Set();
|
|
2191
|
+
const used2 = new Set();
|
|
2192
|
+
// 1. PRIORITIZE LEFT AND RIGHT SIMILARITY (Pair ordering from top to bottom)
|
|
2193
|
+
const remaining1 = [...list1];
|
|
2194
|
+
const remaining2 = [...list2];
|
|
2195
|
+
while (remaining1.length > 0 && remaining2.length > 0) {
|
|
2196
|
+
let bestPair = { i1: -1, i2: -1, score: -1 };
|
|
2197
|
+
for (let i = 0; i < remaining1.length; i++) {
|
|
2198
|
+
for (let j = 0; j < remaining2.length; j++) {
|
|
2199
|
+
const score = this.getSimilarity(remaining1[i], remaining2[j]);
|
|
2200
|
+
if (score > bestPair.score) {
|
|
2201
|
+
bestPair = { i1: i, i2: j, score };
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
if (bestPair.score >= 0.15 && bestPair.i1 !== -1 && bestPair.i2 !== -1) {
|
|
2206
|
+
const item1 = remaining1[bestPair.i1];
|
|
2207
|
+
const item2 = remaining2[bestPair.i2];
|
|
2208
|
+
ordered1.push(item1);
|
|
2209
|
+
ordered2.push(item2);
|
|
2210
|
+
used1.add(item1);
|
|
2211
|
+
used2.add(item2);
|
|
2212
|
+
remaining1.splice(bestPair.i1, 1);
|
|
2213
|
+
remaining2.splice(bestPair.i2, 1);
|
|
2214
|
+
}
|
|
2215
|
+
else {
|
|
2216
|
+
break;
|
|
2217
|
+
}
|
|
2388
2218
|
}
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2219
|
+
// 2. FOR REMAINING UNMATCHED LEFT & RIGHT, ORDER BY RELEVANCE TO ALREADY PLACED ITEMS
|
|
2220
|
+
const unplaced1 = list1.filter(item => !used1.has(item));
|
|
2221
|
+
const unplaced2 = list2.filter(item => !used2.has(item));
|
|
2222
|
+
const rankUnplaced = (items, placedContext) => {
|
|
2223
|
+
return items.map(item => {
|
|
2224
|
+
let maxSim = 0;
|
|
2225
|
+
placedContext.forEach(placed => {
|
|
2226
|
+
const s = this.getSimilarity(item, placed);
|
|
2227
|
+
if (s > maxSim)
|
|
2228
|
+
maxSim = s;
|
|
2229
|
+
});
|
|
2230
|
+
return { item, maxSim };
|
|
2231
|
+
}).sort((a, b) => b.maxSim - a.maxSim).map(x => x.item);
|
|
2232
|
+
};
|
|
2233
|
+
const allPlaced = [...ordered1, ...ordered2];
|
|
2234
|
+
const sortedUnplaced1 = rankUnplaced(unplaced1, allPlaced);
|
|
2235
|
+
const sortedUnplaced2 = rankUnplaced(unplaced2, allPlaced);
|
|
2236
|
+
sortedUnplaced1.forEach(item => {
|
|
2237
|
+
ordered1.push(item);
|
|
2238
|
+
used1.add(item);
|
|
2239
|
+
});
|
|
2240
|
+
sortedUnplaced2.forEach(item => {
|
|
2241
|
+
ordered2.push(item);
|
|
2242
|
+
used2.add(item);
|
|
2243
|
+
});
|
|
2244
|
+
// 3. REARRANGE MIDDLE (CENTER) HIERARCHY TO MATCH EXACT ROW POSITIONS OF LEFT AND RIGHT
|
|
2245
|
+
const orderedCenter = [];
|
|
2246
|
+
const usedCenter = new Set();
|
|
2247
|
+
const maxRows = Math.max(ordered1.length, ordered2.length);
|
|
2248
|
+
for (let k = 0; k < maxRows; k++) {
|
|
2249
|
+
const item1 = ordered1[k];
|
|
2250
|
+
const item2 = ordered2[k];
|
|
2251
|
+
let bestCenterMatch = { item: '', score: -1 };
|
|
2252
|
+
cleanCenter.forEach(cItem => {
|
|
2253
|
+
if (usedCenter.has(cItem))
|
|
2254
|
+
return;
|
|
2255
|
+
const score1 = item1 ? this.getSimilarity(cItem, item1) : 0;
|
|
2256
|
+
const score2 = item2 ? this.getSimilarity(cItem, item2) : 0;
|
|
2257
|
+
const maxScore = Math.max(score1, score2);
|
|
2258
|
+
if (maxScore > bestCenterMatch.score) {
|
|
2259
|
+
bestCenterMatch = { item: cItem, score: maxScore };
|
|
2260
|
+
}
|
|
2261
|
+
});
|
|
2262
|
+
if (bestCenterMatch.score >= 0.45 && bestCenterMatch.item) {
|
|
2263
|
+
orderedCenter.push(bestCenterMatch.item);
|
|
2264
|
+
usedCenter.add(bestCenterMatch.item);
|
|
2265
|
+
}
|
|
2266
|
+
else {
|
|
2267
|
+
orderedCenter.push('-'); // Insert slot spacer so center item pairs directly with its row
|
|
2268
|
+
}
|
|
2392
2269
|
}
|
|
2270
|
+
// Append any remaining center items not yet matched to a row
|
|
2271
|
+
cleanCenter.forEach(cItem => {
|
|
2272
|
+
if (!usedCenter.has(cItem)) {
|
|
2273
|
+
orderedCenter.push(cItem);
|
|
2274
|
+
usedCenter.add(cItem);
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2277
|
+
return { p1: ordered1, p2: ordered2, center: orderedCenter };
|
|
2278
|
+
}
|
|
2279
|
+
onViewProfile(side) {
|
|
2393
2280
|
this.viewProfileClick.emit({ side });
|
|
2394
2281
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
this.
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
this.
|
|
2406
|
-
|
|
2407
|
-
|
|
2282
|
+
getPerson1UniqueInterests() {
|
|
2283
|
+
const raw = this.alignedPerson1Interests.length > 0
|
|
2284
|
+
? this.alignedPerson1Interests
|
|
2285
|
+
: this.displayPerson1Interests.length > 0
|
|
2286
|
+
? this.displayPerson1Interests
|
|
2287
|
+
: this.person1Interests;
|
|
2288
|
+
return raw.filter(item => item && item !== '-' && item !== '----' && !this.isInCenter(item));
|
|
2289
|
+
}
|
|
2290
|
+
getPerson2UniqueInterests() {
|
|
2291
|
+
const raw = this.alignedPerson2Interests.length > 0
|
|
2292
|
+
? this.alignedPerson2Interests
|
|
2293
|
+
: this.displayPerson2Interests.length > 0
|
|
2294
|
+
? this.displayPerson2Interests
|
|
2295
|
+
: this.person2Interests;
|
|
2296
|
+
return raw.filter(item => item && item !== '-' && item !== '----' && !this.isInCenter(item));
|
|
2297
|
+
}
|
|
2298
|
+
getChipOffset(index, column) {
|
|
2299
|
+
const leftOffsets = [0, 20, 6, 26, 12, 30, 4, 18];
|
|
2300
|
+
const rightOffsets = [0, 20, 6, 26, 12, 30, 4, 18];
|
|
2301
|
+
const centerOffsets = [0, -14, 12, -6, 16, -10, 8, -4];
|
|
2302
|
+
if (column === 'left')
|
|
2303
|
+
return leftOffsets[index % leftOffsets.length];
|
|
2304
|
+
if (column === 'right')
|
|
2305
|
+
return rightOffsets[index % rightOffsets.length];
|
|
2306
|
+
return centerOffsets[index % centerOffsets.length];
|
|
2408
2307
|
}
|
|
2409
2308
|
compressConfigImagesIfNeeded() {
|
|
2410
2309
|
const tasks = [];
|
|
@@ -2503,50 +2402,62 @@ class ProfileComparisonLibComponent {
|
|
|
2503
2402
|
const user2FaceCoords = this.getFaceCoordinatesFromBBox(face2);
|
|
2504
2403
|
const user1Eyes = this.getEyeCoordinatesFromFace(face1);
|
|
2505
2404
|
const user2Eyes = this.getEyeCoordinatesFromFace(face2);
|
|
2506
|
-
const leftContainerEl = this.
|
|
2507
|
-
const rightContainerEl = this.
|
|
2405
|
+
const leftContainerEl = this.profileImgLeft?.nativeElement || null;
|
|
2406
|
+
const rightContainerEl = this.profileImgRight?.nativeElement || null;
|
|
2508
2407
|
const containerWidth1 = leftContainerEl?.clientWidth || 160;
|
|
2509
2408
|
const containerHeight1 = leftContainerEl?.clientHeight || 550;
|
|
2510
2409
|
const containerWidth2 = rightContainerEl?.clientWidth || 160;
|
|
2511
2410
|
const containerHeight2 = rightContainerEl?.clientHeight || 550;
|
|
2512
2411
|
const scale1 = this.user1NaturalWidth ? Math.max(containerWidth1 / this.user1NaturalWidth, containerHeight1 / this.user1NaturalHeight) : 1;
|
|
2513
2412
|
const scale2 = this.user2NaturalWidth ? Math.max(containerWidth2 / this.user2NaturalWidth, containerHeight2 / this.user2NaturalHeight) : 1;
|
|
2514
|
-
const displayedFaceHeight1 = face1.height * scale1;
|
|
2515
|
-
const displayedFaceHeight2 = face2.height * scale2;
|
|
2516
|
-
const ratio1 = containerHeight1 ? displayedFaceHeight1 / containerHeight1 : 0;
|
|
2517
|
-
const ratio2 = containerHeight2 ? displayedFaceHeight2 / containerHeight2 : 0;
|
|
2518
|
-
let finalScale1 = ratio1 > 0 && ratio1 < 0.22 ? Math.min(3, 0.32 / ratio1) : 1;
|
|
2519
|
-
let finalScale2 = ratio2 > 0 && ratio2 < 0.22 ? Math.min(3, 0.32 / ratio2) : 1;
|
|
2520
|
-
const effRatio1 = ratio1 * finalScale1;
|
|
2521
|
-
const effRatio2 = ratio2 * finalScale2;
|
|
2522
|
-
if (effRatio1 > effRatio2 * 1.35)
|
|
2523
|
-
finalScale1 = Math.max(0.95, finalScale1 * (effRatio2 / effRatio1));
|
|
2524
|
-
else if (effRatio2 > effRatio1 * 1.35)
|
|
2525
|
-
finalScale2 = Math.max(0.95, finalScale2 * (effRatio1 / effRatio2));
|
|
2526
2413
|
const nose1 = this.getNoseCoordinateFromFace(face1);
|
|
2527
2414
|
const nose2 = this.getNoseCoordinateFromFace(face2);
|
|
2528
2415
|
const anchor1Y = (Math.min(user1Eyes.leftEye.y, user1Eyes.rightEye.y) + nose1.y) / 2;
|
|
2529
2416
|
const anchor2Y = (Math.min(user2Eyes.leftEye.y, user2Eyes.rightEye.y) + nose2.y) / 2;
|
|
2530
|
-
const tCenter1 = this.user1NaturalHeight ? -((anchor1Y - this.user1NaturalHeight / 2) * scale1
|
|
2531
|
-
const tCenter2 = this.user2NaturalHeight ? -((anchor2Y - this.user2NaturalHeight / 2) * scale2
|
|
2532
|
-
|
|
2533
|
-
|
|
2417
|
+
const tCenter1 = this.user1NaturalHeight ? -((anchor1Y - this.user1NaturalHeight / 2) * scale1) : 0;
|
|
2418
|
+
const tCenter2 = this.user2NaturalHeight ? -((anchor2Y - this.user2NaturalHeight / 2) * scale2) : 0;
|
|
2419
|
+
const shiftY1 = tCenter1 - this.eyeAlignmentBiasPx;
|
|
2420
|
+
const shiftY2 = tCenter2 - this.eyeAlignmentBiasPx;
|
|
2421
|
+
// Apply eye-to-eye vertical repositioning transforms without any scaling
|
|
2422
|
+
this.user1Transform = `translateY(${shiftY1}px)`;
|
|
2423
|
+
this.user2Transform = `translateY(${shiftY2}px)`;
|
|
2424
|
+
// Extend image dimensions into the adjusted area without applying any uniform zoom/scaling
|
|
2425
|
+
if (shiftY1 > 0) {
|
|
2426
|
+
this.user1Top = `-${shiftY1}px`;
|
|
2427
|
+
this.user1Height = `calc(100% + ${shiftY1}px)`;
|
|
2428
|
+
}
|
|
2429
|
+
else {
|
|
2430
|
+
this.user1Top = '0px';
|
|
2431
|
+
this.user1Height = `calc(100% + ${Math.abs(shiftY1)}px)`;
|
|
2432
|
+
}
|
|
2433
|
+
if (shiftY2 > 0) {
|
|
2434
|
+
this.user2Top = `-${shiftY2}px`;
|
|
2435
|
+
this.user2Height = `calc(100% + ${shiftY2}px)`;
|
|
2436
|
+
}
|
|
2437
|
+
else {
|
|
2438
|
+
this.user2Top = '0px';
|
|
2439
|
+
this.user2Height = `calc(100% + ${Math.abs(shiftY2)}px)`;
|
|
2440
|
+
}
|
|
2534
2441
|
this.alignmentCalculated = true;
|
|
2535
2442
|
}
|
|
2536
2443
|
applyDefaultAlignment() {
|
|
2537
2444
|
this.user1Transform = 'translateY(0px)';
|
|
2538
2445
|
this.user2Transform = 'translateY(0px)';
|
|
2446
|
+
this.user1Top = '0px';
|
|
2447
|
+
this.user1Height = '100%';
|
|
2448
|
+
this.user2Top = '0px';
|
|
2449
|
+
this.user2Height = '100%';
|
|
2539
2450
|
this.alignmentCalculated = true;
|
|
2540
2451
|
}
|
|
2541
2452
|
isValidFaceData(face) {
|
|
2542
2453
|
return !!face && typeof face.x === 'number' && face.width > 0 && face.height > 0;
|
|
2543
2454
|
}
|
|
2544
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2545
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: ProfileComparisonLibComponent, isStandalone: false, selector: "lib-profile-comparison", inputs: { config: "config", backendMode: "backendMode", backendUrl: "backendUrl", fadeAllEdges: "fadeAllEdges" }, outputs: { matrixDataChange: "matrixDataChange", rawLLMOutputChange: "rawLLMOutputChange", viewProfileClick: "viewProfileClick", edgeDrag: "edgeDrag" }, host: { listeners: { "window:resize": "onResize()" } }, viewQueries: [{ propertyName: "leftContainer", first: true, predicate: ["leftContainer"], descendants: true }, { propertyName: "rightContainer", first: true, predicate: ["rightContainer"], descendants: true }, { propertyName: "leftFrame", first: true, predicate: ["leftFrame"], descendants: true }, { propertyName: "rightFrame", first: true, predicate: ["rightFrame"], descendants: true }, { propertyName: "leftBgFrame", first: true, predicate: ["leftBgFrame"], descendants: true }, { propertyName: "rightBgFrame", first: true, predicate: ["rightBgFrame"], descendants: true }, { propertyName: "shapeContainer", first: true, predicate: ["shapeContainer"], descendants: true }, { propertyName: "shapeTextCenter", first: true, predicate: ["shapeTextCenter"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"configure-backend-message\" *ngIf=\"!backendConfigured\">\n Configure backend\n</div>\n\n<div class=\"profile-screen profile-comparison\" *ngIf=\"backendConfigured\"\n [class.fade-all-edges]=\"config.edgeShading !== false\"\n [style.--edge-shading-color]=\"config.shadingColor || 'rgba(44, 40, 47, 0.0)'\"\n [style.--edge-shading-display]=\"config.edgeShading !== false ? 'block' : 'none'\">\n\n <div class=\"profile-comparison__inner\" #shapeContainer>\n <!-- Backgrounds layer: structurally below everything else to fix stacking -->\n <div class=\"profile-comparison__bg-layer\">\n <div class=\"profile-comparison__frame profile-comparison__frame--left bg-frame\"\n [class.is-animating-nudge]=\"leftShapeAnimating\"\n #leftBgFrame>\n <div\n class=\"profile-comparison__bg profile-comparison__bg--left\"\n [ngStyle]=\"{ 'background-image': 'url(' + user1Image + ')', 'transform': user1Transform, 'background-position': user1ObjectPosition }\"\n ></div>\n </div>\n <div class=\"profile-comparison__frame profile-comparison__frame--right bg-frame\"\n [class.is-animating-nudge]=\"rightShapeAnimating\"\n #rightBgFrame>\n <div\n class=\"profile-comparison__bg profile-comparison__bg--right\"\n [ngStyle]=\"{ 'background-image': 'url(' + user2Image + ')', 'transform': user2Transform, 'background-position': user2ObjectPosition }\"\n ></div>\n </div>\n </div>\n\n <!-- Active interactive shapes and content layer -->\n <div class=\"profile-comparison__frame profile-comparison__frame--left swiper-no-swiping\"\n [class.is-animating-nudge]=\"leftShapeAnimating\"\n (click)=\"onShapeClick('left')\"\n #leftFrame>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--glass\"\n viewBox=\"0 0 256 275\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <g filter=\"url(#filter-glass-left)\">\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 L0 268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924\"\n stroke=\"#61C2AB\"\n stroke-opacity=\"0.3\"\n stroke-width=\"0.809707\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n <defs>\n <filter id=\"filter-glass-left\" x=\"-40\" y=\"-40\" width=\"336\" height=\"355\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect1_dropShadow\" result=\"effect2_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect2_dropShadow\" result=\"effect3_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect3_dropShadow\" result=\"effect4_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect4_dropShadow\" result=\"effect5_dropShadow\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect5_dropShadow\" result=\"shape\"/>\n </filter>\n </defs>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--middle\"\n viewBox=\"0 0 256 276\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <clipPath id=\"clip-middle-left\">\n <path d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z\"/>\n </clipPath>\n <filter id=\"filter0_i_2126_3746\" x=\"-0.638672\" y=\"-2\" width=\"256\" height=\"279\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14.2857 0\" result=\"hardAlpha\" />\n <feOffset />\n <feGaussianBlur stdDeviation=\"7.95\" />\n <feComposite in2=\"hardAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" />\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0.516667 0 0 0 0 1 0 0 0 1 0\" />\n <feBlend mode=\"normal\" in2=\"shape\" result=\"effect1_innerShadow_2126_3746\" />\n </filter>\n </defs>\n <g clip-path=\"url(#clip-middle-left)\" filter=\"url(#filter0_i_2126_3746)\">\n <path\n d=\"M245.538 25.2463 L9.36 -1.2459 Q-0.638672 -2.5 -0.638672 7.5 V266.75 Q-0.638672 276.75 9.16 275.25 L246.042 238.623 C251.404 237.794 255.361 233.161 255.361 227.735 V36.2037 C255.361 30.5841 251.126 25.8476 245.538 25.2463 Z\"\n fill=\"#00CFE6\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--left\"\n viewBox=\"0 0 256 278\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <radialGradient id=\"profile-left-fill\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(65.1034 -81.3964 74.955 49.415 128 139)\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#0051E6\" stop-opacity=\"0\" />\n <stop offset=\"0.5\" stop-color=\"#0051E6\" stop-opacity=\"0.035\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </radialGradient>\n <linearGradient id=\"profile-left-stroke\" x1=\"128\" y1=\"0\" x2=\"128\" y2=\"278\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#52FFEB\" />\n <stop offset=\"0.5\" stop-color=\"#41CFA1\" />\n <stop offset=\"1\" stop-color=\"#319990\" />\n </linearGradient>\n </defs>\n <!-- Fill Layer -->\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z\"\n fill=\"url(#profile-left-fill)\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Stroke Layer (Left outer border skipped) -->\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 L0 268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924\"\n fill=\"none\"\n stroke=\"url(#profile-left-stroke)\"\n stroke-width=\"2\"\n stroke-miterlimit=\"3.99393\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Added explicit embedded SVG text anchoring tightly inside the graphical shape bounds instead of pure flexbox logic padding. -->\n <g\n style=\"cursor: pointer !important;\"\n (click)=\"onViewProfile('left'); $event.stopPropagation()\"\n (mousedown)=\"$event.stopPropagation()\"\n >\n <rect [attr.x]=\"viewProfileLeftX - 15\" y=\"240\" width=\"115\" height=\"40\" fill=\"transparent\" pointer-events=\"auto\" />\n <text\n [attr.x]=\"viewProfileLeftX\" y=\"256\"\n fill=\"rgba(255, 255, 255, 0.60)\"\n font-family=\"'Calistoga', serif\"\n font-size=\"9\"\n text-anchor=\"start\"\n [class.is-animating-bob]=\"leftProfileClicked\"\n style=\"text-shadow: 0 2px 4.2px #000;\"\n pointer-events=\"none\"\n >View Profile</text>\n </g>\n </svg>\n <div class=\"profile-comparison__content profile-comparison__content--left\">\n <div class=\"profile-comparison__list profile-comparison__list--left\">\n <!-- Semantic Items only -->\n <ng-container *ngFor=\"let interest of alignedPerson1Interests; let i = index\">\n <div class=\"profile-comparison__item profile-comparison__item--left\">\n <lib-marquee\n class=\"profile-comparison__text\"\n [title]=\"interest\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 100; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n\n <!-- Dash beneath each interest -->\n <div class=\"profile-comparison__dash-container\" [ngSwitch]=\"getDashType(i)\">\n <!-- High Similarity: Short single dash, 0.3 opacity -->\n <svg *ngSwitchCase=\"'high'\" class=\"profile-comparison__dash profile-comparison__dash--high\" width=\"15\" height=\"4\" viewBox=\"0 0 15 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H13\" stroke=\"white\" stroke-width=\"4\" stroke-opacity=\"0.3\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Medium Similarity: Standard 1 dash -->\n <svg *ngSwitchCase=\"'medium'\" class=\"profile-comparison__dash profile-comparison__dash--medium\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Low Similarity: 4 small dashes -->\n <svg *ngSwitchCase=\"'low'\" class=\"profile-comparison__dash profile-comparison__dash--low\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-dasharray=\"2,5\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <div class=\"profile-comparison__frame profile-comparison__frame--right swiper-no-swiping\"\n [class.is-animating-nudge]=\"rightShapeAnimating\"\n (click)=\"onShapeClick('right')\"\n #rightFrame>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--glass\"\n viewBox=\"0 0 257 275\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <g filter=\"url(#filter-glass-right)\">\n <path\n d=\"M256.24 267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 L256.24 267.5\"\n stroke=\"#AB4AFF\"\n stroke-opacity=\"0.3\"\n stroke-width=\"0.809707\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n <defs>\n <filter id=\"filter-glass-right\" x=\"-40\" y=\"-40\" width=\"337\" height=\"355\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect1_dropShadow\" result=\"effect2_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect2_dropShadow\" result=\"effect3_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect3_dropShadow\" result=\"effect4_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect4_dropShadow\" result=\"effect5_dropShadow\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect5_dropShadow\" result=\"shape\"/>\n </filter>\n </defs>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--middle\"\n viewBox=\"0 0 257 276\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <clipPath id=\"clip-middle-right\">\n <path d=\"M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95935 237.368 -9.15527e-05 232.733 -9.15527e-05 227.305 V36.1607 C-9.15527e-05 30.5399 4.23741 25.8028 9.8262 26.4454 Z\"/>\n </clipPath>\n <filter id=\"filter0_i_2126_3750\" x=\"0\" y=\"-2\" width=\"256.24\" height=\"279\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14.2857 0\" result=\"hardAlpha\" />\n <feOffset />\n <feGaussianBlur stdDeviation=\"7.95\" />\n <feComposite in2=\"hardAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" />\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.482353 0 0 0 0 0 0 0 0 0 0.901961 0 0 0 1 0\" />\n <feBlend mode=\"normal\" in2=\"shape\" result=\"effect1_innerShadow_2126_3750\" />\n </filter>\n </defs>\n <g clip-path=\"url(#clip-middle-right)\" filter=\"url(#filter0_i_2126_3750)\">\n <path\n d=\"M9.82611 25.203 L244.066 0.0640762 L246.24 -0.05 Q256.24 -1.2424 256.24 8.75 V266.25 Q256.24 276.258 246.24 274.75 L243.564 274.304 L9.32402 238.195 C3.95935 237.368 -9.15527e-05 232.733 -9.15527e-05 227.305 V36.1607 C-9.15527e-05 30.5399 4.23741 25.8028 9.82611 25.203 Z\"\n fill=\"#7B00E6\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--right\"\n viewBox=\"0 0 257 278\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <radialGradient id=\"profile-right-fill\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(-65.1644 -81.25 -75.0252 49.3261 128.12 138.75)\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#0051E6\" stop-opacity=\"0\" />\n <stop offset=\"0.5\" stop-color=\"#0051E6\" stop-opacity=\"0.035\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </radialGradient>\n <linearGradient id=\"profile-right-stroke\" x1=\"128.12\" y1=\"277.5\" x2=\"128.12\" y2=\"8.50002\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#662C99\" />\n <stop offset=\"0.5\" stop-color=\"#893BD3\" />\n <stop offset=\"1\" stop-color=\"#AB4AFF\" />\n </linearGradient>\n </defs>\n <!-- Fill Layer -->\n <path\n d=\"M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z\"\n fill=\"url(#profile-right-fill)\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Stroke Layer (Right outer border skipped) -->\n <path\n d=\"M256.24 267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 L256.24 267.5\"\n fill=\"none\"\n stroke=\"url(#profile-right-stroke)\"\n stroke-width=\"2\"\n stroke-miterlimit=\"3.99393\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n <g\n style=\"cursor: pointer !important;\"\n (click)=\"onViewProfile('right'); $event.stopPropagation()\"\n (mousedown)=\"$event.stopPropagation()\"\n >\n <rect [attr.x]=\"viewProfileRightX - 100\" y=\"240\" width=\"115\" height=\"40\" fill=\"transparent\" pointer-events=\"auto\" />\n <text\n [attr.x]=\"viewProfileRightX\" y=\"256\"\n fill=\"rgba(255, 255, 255, 0.60)\"\n font-family=\"'Calistoga', serif\"\n font-size=\"9\"\n text-anchor=\"end\"\n [class.is-animating-bob]=\"rightProfileClicked\"\n style=\"text-shadow: 0 2px 4.2px #000;\"\n pointer-events=\"auto\"\n >View Profile</text>\n </g>\n </svg>\n <div class=\"profile-comparison__content profile-comparison__content--right\">\n <div class=\"profile-comparison__list profile-comparison__list--right\">\n <!-- Semantic Items only -->\n <ng-container *ngFor=\"let interest of alignedPerson2Interests; let i = index\">\n <div class=\"profile-comparison__item profile-comparison__item--right\">\n <lib-marquee\n class=\"profile-comparison__text\"\n [title]=\"interest\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 100; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n\n <!-- Dash beneath each interest -->\n <div class=\"profile-comparison__dash-container\" [ngSwitch]=\"getDashType(i)\">\n <!-- High Similarity: Short single dash, 0.5 opacity -->\n <svg *ngSwitchCase=\"'high'\" class=\"profile-comparison__dash profile-comparison__dash--high\" width=\"15\" height=\"4\" viewBox=\"0 0 15 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H13\" stroke=\"white\" stroke-width=\"4\" stroke-opacity=\"0.5\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Medium Similarity: Standard 1 dash -->\n <svg *ngSwitchCase=\"'medium'\" class=\"profile-comparison__dash profile-comparison__dash--medium\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Low Similarity: 4 small dashes -->\n <svg *ngSwitchCase=\"'low'\" class=\"profile-comparison__dash profile-comparison__dash--low\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-dasharray=\"2,5\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <!-- Center text section -->\n <div class=\"profile-comparison__center\" #shapeTextCenter>\n <div class=\"profile-comparison__center-inner\">\n <!-- Exact Items only -->\n <div *ngFor=\"let item of centerItem\" class=\"profile-comparison__center-item\">\n <lib-marquee\n class=\"profile-comparison__center-text\"\n [title]=\"item\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 700; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n <!-- Invisible dash as requested -->\n <div class=\"profile-comparison__center-dash-placeholder\"></div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Loading indicator for alignment process -->\n <div *ngIf=\"isAligning\" class=\"loading-indicator\">\n <div class=\"loading-spinner\"></div>\n </div>\n</div>\n\n\n", styles: ["@keyframes textDropIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes nudgeLeft{0%,to{transform:translate(-24%) scale(.86)}50%{transform:translate(-26%) scale(.86)}}@keyframes nudgeRight{0%,to{transform:translate(24%) scale(.86)}50%{transform:translate(26%) scale(.86)}}.profile-img.left{-webkit-mask-image:linear-gradient(to right,rgb(0,0,0) 0%,rgb(0,0,0) calc(100% - var(--overlap-size, 40px)),rgba(0,0,0,0) 100%);-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-image:linear-gradient(to right,#fff 0% calc(100% - var(--overlap-size, 40px)),#fff0);mask-size:100% 100%;mask-repeat:no-repeat;margin-right:calc(-.5 * var(--overlap-size, 40px))}.profile-img.right{-webkit-mask-image:linear-gradient(to left,rgb(0,0,0) 0%,rgb(0,0,0) calc(100% - var(--overlap-size, 40px)),rgba(0,0,0,0) 100%);-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-image:linear-gradient(to left,#fff 0% calc(100% - var(--overlap-size, 40px)),#fff0);mask-size:100% 100%;mask-repeat:no-repeat;margin-left:calc(-.5 * var(--overlap-size, 40px))}.profile-img.fade-all{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent,black 15%,black 85%,transparent);-webkit-mask-composite:source-in;mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent,black 15%,black 85%,transparent);mask-composite:intersect}.profile-img.fade-all.left{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%)}.profile-img.fade-all.right{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to left,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to left,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%)}.configure-backend-message{padding:1rem;text-align:center;color:#bdc3c7;background:#34495e;border-radius:8px}.profile-screen{width:100%;max-width:460px;margin:0 auto;overflow:visible;position:relative}.profile-flex{display:flex;width:100%;height:auto;align-items:center;overflow:hidden;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);gap:0;margin:0;padding:0;position:relative;box-shadow:none;border:none;--overlap-size: 40px}.profile-flex.fade-all{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 15%,black 85%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);-webkit-mask-composite:intersect;mask-image:linear-gradient(to right,transparent 0%,black 15%,black 85%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);mask-composite:intersect}.shape-bg{position:relative;pointer-events:auto;width:100%;height:100%}.shape-bg1,.shape-bg2{position:absolute;width:75%;max-width:none;height:350px;opacity:1;transition:transform .2s ease-out}.shape-bg1{z-index:1;left:0}.shape-bg2{right:0}.shape-bg.fade-all .shape-bg1{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%)}.shape-bg.fade-all .shape-bg2{-webkit-mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%)}.shape-bg svg{cursor:grab;overflow:visible!important}.shape-bg svg:active{cursor:grabbing}.profile-img{width:50%;height:350px;position:relative;flex-shrink:0;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);margin:0;padding:0;left:0;top:0;border:none;box-sizing:border-box;overflow:hidden;box-shadow:none;outline:none}.profile-img img{transition:transform .3s ease;width:100%;height:100%;object-fit:cover;display:block;object-position:center 30%;opacity:.75;transform-origin:center center;border:none;outline:none;box-shadow:none;position:relative;top:0;left:0;filter:contrast(1.2) brightness(1.1) saturate(1.1);image-rendering:auto}.shape{position:absolute;top:32px;width:100%;left:0;z-index:2;pointer-events:none}.shape-bg{position:relative;pointer-events:auto}.shape-text{position:absolute;top:0;left:0;right:0;width:100%;height:100%;z-index:1;padding:0 1.25rem;box-sizing:border-box;pointer-events:none}.shape-text-left{margin-top:40px;text-align:left;position:absolute;top:0;bottom:0;right:calc(50% + 75px);z-index:2;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-end}.shape-text-left .scroll-container{width:100%}.shape-text-center{position:absolute;left:0;right:0;margin:0 auto;width:fit-content;font-family:Gilroy,sans-serif;font-weight:700;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;text-align:center;display:flex;justify-content:center;align-items:center;flex-direction:column;text-shadow:0 2px 4.2px #000;z-index:10;transition:transform .3s ease;pointer-events:none;top:50%;transform:translateY(-50%);padding:0 .625rem;margin-top:3.8125rem}.shape-text-right{margin-top:40px;text-align:right;position:absolute;top:0;bottom:0;left:calc(50% + 70px);z-index:2;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-start}.shape-text-right .scroll-container{width:100%}.shape-p{font-family:Gilroy,sans-serif;font-weight:400;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;padding-bottom:.125rem;text-align:right;text-shadow:0 2px 4.2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;direction:rtl;-webkit-user-select:none;user-select:none;margin:0}.shape-p-center{font-family:Gilroy,sans-serif;font-weight:700;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;margin:0;text-align:center;display:flex;justify-content:center;align-items:center;text-shadow:0 2px 4.2px #000}.shape-p-right{font-family:Gilroy,sans-serif;font-weight:400;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;padding-bottom:.125rem;text-align:left;text-shadow:0 2px 4.2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0;-webkit-user-select:none;user-select:none}.dash-item{color:#ffffff4d!important;font-size:.625rem!important;height:.625rem}.spacer-item{height:auto!important;color:#fffc!important;font-weight:700;font-size:.75rem!important;letter-spacing:2px}.shape-h2{font-family:Calistoga,serif;font-weight:400;font-size:.625rem;letter-spacing:0%;color:#fff;text-shadow:0 2px 4.2px #000;cursor:pointer;transition:all .3s ease;border:none;outline:none;white-space:nowrap;pointer-events:auto;z-index:100;position:relative;margin-left:.5rem}.shape-h2:hover{opacity:.8;transform:translateY(-2px)}.shape-h2-right{font-family:Calistoga,serif;font-weight:400;font-size:.625rem;letter-spacing:0%;color:#fff;text-shadow:0 2px 4.2px #000;cursor:pointer;transition:all .3s ease;border:none;outline:none;white-space:nowrap;pointer-events:auto;z-index:100;position:relative;margin-right:3.75rem}.shape-h2-right:hover{opacity:.8;transform:translateY(-2px)}.scroll-when-long{display:inline-block;max-width:4.5625rem;white-space:nowrap;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}.scroll-when-long::-webkit-scrollbar{height:2px}.scroll-when-long::-webkit-scrollbar-track{background:#eee;border-radius:.625rem}.scroll-when-long::-webkit-scrollbar-thumb{background:#888;border-radius:.625rem}.scroll-when-long::-webkit-scrollbar-thumb:hover{background:#555}.shape-text p{cursor:default;-webkit-user-select:none;user-select:none}.shape-text p:active{cursor:default}:host{display:block}.profile-comparison{width:100%;padding:0;box-sizing:border-box;display:flex;justify-content:center;align-items:center;overflow:visible}.profile-comparison__inner{position:relative;width:100%;min-width:25.75rem;max-width:100%;min-height:500px;display:grid;grid-template-columns:1fr;grid-template-rows:auto;background:transparent;overflow:visible}.profile-comparison__bg-layer{grid-area:1/1;position:relative;width:100%;height:100%;min-height:inherit;pointer-events:none;display:grid;grid-template-columns:1fr;grid-template-rows:1fr}.profile-comparison__frame{grid-area:1/1;position:relative;pointer-events:none}.profile-comparison__frame.bg-frame{width:100%;height:100%;min-height:inherit;overflow:hidden;border-radius:10px;-webkit-mask-composite:source-in;mask-composite:intersect;will-change:auto}.profile-comparison__frame.bg-frame.profile-comparison__frame--left{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);-webkit-mask-size:100% 100%,100% 100%;mask-size:100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.fade-all-edges .profile-comparison__frame.bg-frame.profile-comparison__frame--left.edge-at-left-border{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to right,transparent 0%,black 20px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to right,transparent 0%,black 20px,black 100%);-webkit-mask-size:100% 100%,100% 100%,100% 100%;mask-size:100% 100%,100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__frame.bg-frame.profile-comparison__frame--right{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);-webkit-mask-size:100% 100%,100% 100%;mask-size:100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.fade-all-edges .profile-comparison__frame.bg-frame.profile-comparison__frame--right.edge-at-right-border{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to left,transparent 0%,black 20px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to left,transparent 0%,black 20px,black 100%);-webkit-mask-size:100% 100%,100% 100%,100% 100%;mask-size:100% 100%,100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__frame--left{transform:translate(-24%) scale(.86)}.profile-comparison__frame--left.is-animating-nudge{animation:nudgeLeft .5s ease-out}.profile-comparison__frame--left .profile-comparison__frame-svg{-webkit-mask-image:var(--edge-mask-left, linear-gradient(to right, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)));mask-image:var(--edge-mask-left, linear-gradient(to right, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)))}.profile-comparison__frame--left .profile-comparison__frame-svg--glass{transform:scale(1.01) translateY(-2px);transform-origin:center;-webkit-mask-image:none;mask-image:none}.profile-comparison__frame--right{transform:translate(24%) scale(.86)}.profile-comparison__frame--right.is-animating-nudge{animation:nudgeRight .5s ease-out}.profile-comparison__frame--right .profile-comparison__frame-svg{-webkit-mask-image:var(--edge-mask-right, linear-gradient(to left, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)));mask-image:var(--edge-mask-right, linear-gradient(to left, transparent, black var(--edge-shading-width, 28px)))}.profile-comparison__frame--right .profile-comparison__frame-svg--glass{transform:scale(1.01) translate(-1px,-2px);transform-origin:center;-webkit-mask-image:none;mask-image:none}.profile-comparison__frame--right .profile-comparison__frame-svg--bottom{width:113.2%}.profile-comparison__frame-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:auto;overflow:visible}.profile-comparison__frame-svg--glass{z-index:1}.profile-comparison__frame-svg--middle{z-index:2;mix-blend-mode:screen}.profile-comparison__frame-svg--left,.profile-comparison__frame-svg--right{z-index:3}.profile-comparison__frame-svg--bottom{width:113.7%;height:112.6%;inset:51% auto auto 50%;transform:translate(-50%,-51%)}.profile-comparison__bg{position:absolute;top:0;left:0;right:0;height:130%;background-size:cover;background-repeat:no-repeat;background-position:center 30%;pointer-events:auto;opacity:.8;transition:filter .6s ease,opacity .6s ease}.profile-comparison__bg--left{background-position:right center;margin-right:0%;-webkit-mask-image:linear-gradient(to right,black 50%,transparent 100%);mask-image:linear-gradient(to right,black 50%,transparent 100%);-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__bg--right{background-position:left center;margin-left:0%;-webkit-mask-image:linear-gradient(to left,black 50%,transparent 100%);mask-image:linear-gradient(to left,black 50%,transparent 100%);-webkit-mask-composite:source-in;mask-composite:intersect}@keyframes viewProfileBob{0%{transform:translateY(0)}50%{transform:translateY(-3px)}to{transform:translateY(0)}}.profile-comparison svg text{transition:opacity .3s ease}.profile-comparison svg text.is-animating-bob{animation:viewProfileBob .6s ease-in-out;fill:#fff!important}.profile-comparison__content{position:relative;z-index:20;padding:50px 12% 75px;min-height:100%;display:flex;flex-direction:column;justify-content:flex-start;color:#ecf0f1;font-family:Gilroy,serif;pointer-events:none}.profile-comparison__content--left{align-items:flex-end;text-align:right;padding-right:29%}.profile-comparison__content--right{align-items:flex-start;text-align:left;padding-left:29%}.profile-comparison__list{display:flex;flex-direction:column;gap:2px;width:100%;pointer-events:auto}.profile-comparison__list--left{align-items:flex-end;text-align:right;padding-right:29%}.profile-comparison__list--right{align-items:flex-start;text-align:left;padding-left:29%}.profile-comparison__item{display:flex;flex-direction:column;height:auto;width:11rem;flex-shrink:0;gap:6px;margin-bottom:6px;animation:textDropIn .4s ease-out backwards}.profile-comparison__item--left{align-items:flex-end;text-align:right}.profile-comparison__item--right{align-items:flex-start;text-align:left}.profile-comparison__text{font-family:Gilroy,sans-serif;font-size:15px;font-weight:100!important;color:#fff;width:100%;display:block;text-shadow:0 2px 4.2px #000}.profile-comparison__text ::ng-deep *{font-weight:400!important;text-shadow:0 2px 4.2px #000}.profile-comparison__dash-container{display:flex;width:100%;height:8px;align-items:center}.profile-comparison__item--left .profile-comparison__dash-container{justify-content:flex-end}.profile-comparison__item--right .profile-comparison__dash-container{justify-content:flex-start}.profile-comparison__dash{display:block}.profile-comparison__center-empty,.profile-comparison__center-dash-placeholder{height:8px;width:100%}.profile-comparison__center{grid-area:1/1;display:flex;align-items:flex-start;justify-content:center;z-index:15;pointer-events:none;transform:scale(.86)}.profile-comparison__center-inner{display:flex;flex-direction:column;align-items:center;text-align:center;justify-content:flex-start;padding:50px 0 75px;gap:6px;width:100%}.profile-comparison__center-item{height:auto;min-height:22px;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;width:9rem;flex-shrink:0;gap:6px;margin-bottom:6px;animation:textDropIn .4s ease-out backwards}.profile-comparison__center-text{font-family:Gilroy,sans-serif;font-size:15px;font-weight:700;color:#fff;width:100%;display:block;text-shadow:0 2px 4.2px #000}.profile-comparison__center-text--empty{color:#ecf0f180}.loading-indicator{position:fixed;top:25%;left:50%;transform:translate(-50%,-50%);background:transparent;color:#fff;padding:20px 30px;border-radius:10px;text-align:center;z-index:1000;display:flex;flex-direction:column;align-items:center;gap:8px}.loading-spinner{width:40px;height:40px;border:4px solid rgba(255,255,255,.3);border-top:4px solid #667eea;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-indicator p{margin:0;font-size:16px;font-weight:500}.api-key-modal-overlay{position:fixed;inset:0;background:#0f172a99;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:2000}.api-key-modal{width:min(640px,92vw);background:#0f172a;color:#e2e8f0;border:1px solid #334155;border-radius:12px;box-shadow:0 20px 50px #00000073;padding:16px 18px}.api-key-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.api-key-modal-header h2{margin:0;font-size:20px;font-weight:700;color:#e2e8f0}.warning-icon{color:#fbbf24}.api-key-modal .close-btn{background:#0b1220;border:1px solid #334155;color:#94a3b8;border-radius:8px;padding:6px;cursor:pointer;line-height:0}.api-key-modal-body{margin-top:12px;display:flex;flex-direction:column;gap:10px}.modal-message,.modal-instruction{margin:0;color:#cbd5e1}.modal-message strong{color:#e5e7eb}.api-key-input-group{display:flex;align-items:center;gap:10px}.api-key-input{flex:1 1 auto;background:#0a1020;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:10px 12px}.api-key-input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb33}.api-key-load-btn{background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600}.api-key-load-btn:hover{background:#1d4ed8}.modal-hint{display:flex;align-items:center;gap:8px;color:#94a3b8}.modal-hint a{color:#93c5fd}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: i3.MarqueeComponent, selector: "lib-marquee", inputs: ["title", "searchText", "padding", "onlyMarqueeOnHover"], outputs: ["marquee"] }] });
|
|
2455
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibComponent, deps: [{ token: ProfileComparisonBackendService }, { token: i0.Renderer2 }, { token: FileConversionService }, { token: ImageCompressionService }, { token: i0.ChangeDetectorRef }, { token: PROFILE_COMPARISON_VERBOSE_LOGGING, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
2456
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: ProfileComparisonLibComponent, isStandalone: false, selector: "lib-profile-comparison", inputs: { config: "config", backendMode: "backendMode", backendUrl: "backendUrl", fadeAllEdges: "fadeAllEdges", person1Name: "person1Name", person2Name: "person2Name" }, outputs: { matrixDataChange: "matrixDataChange", rawLLMOutputChange: "rawLLMOutputChange", viewProfileClick: "viewProfileClick" }, viewQueries: [{ propertyName: "leftContainer", first: true, predicate: ["leftContainer"], descendants: true }, { propertyName: "rightContainer", first: true, predicate: ["rightContainer"], descendants: true }, { propertyName: "profileFlex", first: true, predicate: ["profileFlex"], descendants: true }, { propertyName: "profileImgLeft", first: true, predicate: ["profileImgLeft"], descendants: true }, { propertyName: "profileImgRight", first: true, predicate: ["profileImgRight"], descendants: true }, { propertyName: "shapeContainer", first: true, predicate: ["shapeContainer"], descendants: true }, { propertyName: "shapeBg", first: true, predicate: ["shapeBg"], descendants: true }, { propertyName: "shapeBg1", first: true, predicate: ["shapeBg1"], descendants: true }, { propertyName: "shapeBg2", first: true, predicate: ["shapeBg2"], descendants: true }, { propertyName: "shapeTextLeft", first: true, predicate: ["shapeTextLeft"], descendants: true }, { propertyName: "shapeTextRight", first: true, predicate: ["shapeTextRight"], descendants: true }, { propertyName: "shapeTextCenter", first: true, predicate: ["shapeTextCenter"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"configure-backend-message\" *ngIf=\"!backendConfigured\">\n Configure backend\n</div>\n\n<div class=\"profile-screen\" *ngIf=\"backendConfigured\">\n <div #profileFlex class=\"profile-flex\" [class.fade-all]=\"fadeAllEdges\">\n <svg class=\"image-noise-overlay\" style=\"position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: hidden; z-index: 0;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 320\" fill=\"none\" preserveAspectRatio=\"none\">\n <g filter=\"url(#filter0_n_1_1753)\">\n <rect width=\"100%\" height=\"100%\" fill=\"url(#paint0_radial_1_1753)\" fill-opacity=\"0.33\"/>\n </g>\n <defs>\n <filter id=\"filter0_n_1_1753\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\" filterUnits=\"objectBoundingBox\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\"/>\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.10869565606117249 0.10869565606117249\" stitchTiles=\"stitch\" numOctaves=\"3\" result=\"noise\" seed=\"7339\"/>\n <feColorMatrix in=\"noise\" type=\"luminanceToAlpha\" result=\"alphaNoise\"/>\n <feComponentTransfer in=\"alphaNoise\" result=\"coloredNoise1\">\n <feFuncA type=\"discrete\" tableValues=\"0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \"/>\n </feComponentTransfer>\n <feComposite operator=\"in\" in2=\"shape\" in=\"coloredNoise1\" result=\"noise1Clipped\"/>\n <feFlood flood-color=\"rgba(0, 0, 0, 0.25)\" result=\"color1Flood\"/>\n <feComposite operator=\"in\" in2=\"noise1Clipped\" in=\"color1Flood\" result=\"color1\"/>\n <feMerge result=\"effect1_noise_1_1753\">\n <feMergeNode in=\"shape\"/>\n <feMergeNode in=\"color1\"/>\n </feMerge>\n </filter>\n <radialGradient id=\"paint0_radial_1_1753\" cx=\"50%\" cy=\"50%\" r=\"50%\" fx=\"50%\" fy=\"50%\">\n <stop offset=\"0%\" stop-opacity=\"0\"/>\n <stop offset=\"100%\" stop-opacity=\"0.25\"/>\n </radialGradient>\n </defs>\n </svg>\n <div #profileImgLeft class=\"profile-img left\" [class.fade-all]=\"fadeAllEdges\">\n <img\n [src]=\"user1Image\"\n alt=\"User 1\"\n [style.transform]=\"user1Transform\"\n [style.top]=\"user1Top\"\n [style.height]=\"user1Height\"\n [style.object-position]=\"user1ObjectPosition\"\n (load)=\"onUserImageLoad(1, $event)\"\n />\n </div>\n <div #profileImgRight class=\"profile-img right\" [class.fade-all]=\"fadeAllEdges\">\n <img\n [src]=\"user2Image\"\n alt=\"User 2\"\n [style.transform]=\"user2Transform\"\n [style.top]=\"user2Top\"\n [style.height]=\"user2Height\"\n [style.object-position]=\"user2ObjectPosition\"\n (load)=\"onUserImageLoad(2, $event)\"\n />\n </div>\n <div class=\"image-shade-overlay\"></div>\n\n <!-- Frosted Glass Overlay Layer -->\n <div class=\"overlay-container\">\n <!-- Top Headers -->\n <div class=\"header-label left\">{{ person1Name }}</div>\n <div class=\"header-label center\">Shared</div>\n <div class=\"header-label right\">{{ person2Name }}</div>\n\n <!-- Chips Columns Grid -->\n <div class=\"chips-grid\">\n <!-- Person 1 Unique Interests -->\n <div class=\"chips-column left\">\n <ng-container *ngFor=\"let interest of getPerson1UniqueInterests(); let i = index\">\n <div class=\"frosted-chip person-a\" [style.margin-right.px]=\"getChipOffset(i, 'left')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Shared Interests (Center Column) -->\n <div class=\"chips-column center\">\n <ng-container *ngFor=\"let interest of centerItem; let i = index\">\n <div class=\"frosted-chip shared\" *ngIf=\"interest && interest !== '-' && interest !== '----'\" [style.margin-left.px]=\"getChipOffset(i, 'center')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Person 2 Unique Interests -->\n <div class=\"chips-column right\">\n <ng-container *ngFor=\"let interest of getPerson2UniqueInterests(); let i = index\">\n <div class=\"frosted-chip person-b\" [style.margin-left.px]=\"getChipOffset(i, 'right')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Bottom Actions -->\n <div class=\"view-profile left\" (click)=\"onViewProfile('left')\">View Profile</div>\n <div class=\"view-profile right\" (click)=\"onViewProfile('right')\">View Profile</div>\n </div>\n\n <!-- Loading indicator for alignment process -->\n <div *ngIf=\"isAligning\" class=\"loading-indicator\">\n <div class=\"loading-spinner\"></div>\n </div>\n</div>\n\n\n", styles: [":host{display:block;width:100%;max-width:100%;overflow-x:hidden!important;box-sizing:border-box!important}.profile-img.left{position:absolute;top:0;bottom:0;left:0;width:58%;height:100%;margin:0;z-index:1;-webkit-mask-image:linear-gradient(to right,black 0%,black 45%,transparent 92%);mask-image:linear-gradient(to right,black 0%,black 45%,transparent 92%)}.profile-img.right{position:absolute;inset:0 0 0 auto;width:58%;height:100%;margin:0;z-index:2;-webkit-mask-image:linear-gradient(to right,transparent 8%,black 55%,black 100%);mask-image:linear-gradient(to right,transparent 8%,black 55%,black 100%)}@media(min-width:768px){.profile-img.left{width:65%;-webkit-mask-image:linear-gradient(to right,black 0%,black 40%,transparent 90%);mask-image:linear-gradient(to right,black 0%,black 40%,transparent 90%)}.profile-img.right{width:65%;-webkit-mask-image:linear-gradient(to right,transparent 10%,black 60%,black 100%);mask-image:linear-gradient(to right,transparent 10%,black 60%,black 100%)}}.configure-backend-message{padding:1rem;text-align:center;color:#bdc3c7;background:#34495e;border-radius:8px}.backend-error-message{padding:.75rem 1rem;text-align:center;color:#fef3c7;background:#b4530940;border:1px solid rgba(245,158,11,.5);border-radius:8px;margin-bottom:.5rem}.profile-screen{width:100%;max-width:768px;margin:0 auto;overflow:hidden;position:relative;border-radius:12px}.profile-flex{display:flex;width:100%;height:440px;overflow:hidden;background:transparent;gap:0;margin:0;padding:0;position:relative;box-shadow:none;border:none;--overlap-size: 40px}.profile-flex.fade-all{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 18%,black 82%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);-webkit-mask-composite:intersect;mask-image:linear-gradient(to right,transparent 0%,black 18%,black 82%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);mask-composite:intersect}.profile-flex .image-shade-overlay{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:3;background:radial-gradient(ellipse at center,transparent 40%,rgba(41,37,45,.25) 100%)}.profile-flex .image-noise-overlay{position:absolute;inset:0;width:100%;height:100%;max-width:100%;max-height:100%;pointer-events:none;overflow:hidden;z-index:0}.shape-bg1,.shape-bg2{position:absolute;max-width:250px;opacity:1;transition:transform .2s ease-out}.shape-bg1{z-index:1;left:0}.shape-bg2{right:0}.shape-bg.fade-all .shape-bg1{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%)}.shape-bg.fade-all .shape-bg2{-webkit-mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%)}.shape-bg svg{cursor:grab}.shape-bg svg:active{cursor:grabbing}.profile-img{height:100%;position:relative;flex-shrink:0;background:transparent;margin:0;padding:0;left:0;top:0;border:none;box-sizing:border-box;overflow:hidden;box-shadow:none;outline:none}.profile-img img{transition:transform .3s ease;width:100%;height:100%;object-fit:cover;display:block;object-position:center center;opacity:1;transform-origin:center center;border:none;outline:none;box-shadow:none;position:relative;top:0;left:0;filter:contrast(1.2) brightness(1.1) saturate(1.1);image-rendering:auto}.overlay-container{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:10;-webkit-user-select:none;user-select:none;overflow:hidden;background:#29252da6;backdrop-filter:blur(.5px);-webkit-backdrop-filter:blur(.5px)}.header-label{position:absolute;top:20px;font-family:Gilroy,sans-serif;text-shadow:0 1px 8px rgba(0,0,0,.8);pointer-events:none}.header-label.left{left:5%;transform:none;font-size:14px;font-weight:400;color:#fffffff2}.header-label.center{left:50%;transform:translate(-50%);font-size:12px;font-weight:400;color:#fffc;letter-spacing:0}.header-label.right{right:5%;left:auto;transform:none;font-size:14px;font-weight:400;color:#fffffff2}.chips-grid{position:absolute;inset:45px 0;display:grid;grid-template-columns:repeat(3,1fr);padding:0;pointer-events:none;box-sizing:border-box}.chips-column{display:flex;flex-direction:column;gap:8px;max-height:100%;overflow-y:auto;padding:4px 10px;box-sizing:border-box;width:100%;scrollbar-width:none}.chips-column::-webkit-scrollbar{display:none}.chips-column.left{align-items:flex-end}.chips-column.center{align-items:center}.chips-column.right{align-items:flex-start}.frosted-chip{position:relative;cursor:pointer;pointer-events:auto;transition:transform .2s ease,box-shadow .2s ease,background .2s ease;flex-shrink:0;padding:6px 18px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;border:none;background:#ffffff30;box-shadow:inset 0 1.5px 2px #ffffff59;backdrop-filter:blur(.5px);-webkit-backdrop-filter:blur(.5px)}.frosted-chip:hover{transform:scale(1.04);background:#ffffff29}.frosted-chip .chip-label{position:relative;color:#a8a8a8;text-align:center;font-family:Gilroy,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:140%;letter-spacing:-.01em;text-shadow:none;white-space:nowrap;pointer-events:none;z-index:1}.view-profile{position:absolute;bottom:40px!important;color:#ffffff80;text-shadow:0 2px 4.2px #000;font-family:Calistoga,serif;font-size:18px;font-style:normal;font-weight:400;line-height:normal;cursor:pointer;pointer-events:auto;transition:opacity .2s ease,transform .2s ease}.view-profile:hover{opacity:.85}.view-profile.left{left:5%;transform:none}.view-profile.left:hover{transform:translateY(-2px)}.view-profile.right{right:5%;left:auto;transform:none}.view-profile.right:hover{transform:translateY(-2px)}#drag-preview{position:absolute;pointer-events:none;display:none;background:#000000b3;color:#fff;padding:5px 10px;border-radius:5px;z-index:9999;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shape-text-container{width:70px;position:relative;overflow:hidden}.draggable{width:70px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:grab;-webkit-user-select:none;user-select:none;position:relative;display:inline-block}.dragging{width:auto;text-overflow:clip;cursor:grabbing;position:absolute;z-index:1000}.p-wrapper{width:70px;overflow-x:hidden}.shape-text-center{display:flex;justify-content:space-between;flex-direction:column;gap:10px;height:100%;margin-top:55px}.loading-indicator{position:absolute;inset:0;width:100%;height:100%;background:#00000040;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);color:#fff;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;pointer-events:auto}.loading-spinner{width:44px;height:44px;border:3.5px solid rgba(255,255,255,.25);border-top:3.5px solid #ffffff;border-radius:50%;animation:spin .8s linear infinite;box-shadow:0 0 15px #fff3}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-indicator p{margin:0;font-size:14px;font-weight:500;color:#ffffffd9;letter-spacing:.02em}.api-key-modal-overlay{position:fixed;inset:0;background:#0f172a99;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:2000}.api-key-modal{width:min(640px,92vw);background:#0f172a;color:#e2e8f0;border:1px solid #334155;border-radius:12px;box-shadow:0 20px 50px #00000073;padding:16px 18px}.api-key-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.api-key-modal-header h2{margin:0;font-size:20px;font-weight:700;color:#e2e8f0}.warning-icon{color:#fbbf24}.api-key-modal .close-btn{background:#0b1220;border:1px solid #334155;color:#94a3b8;border-radius:8px;padding:6px;cursor:pointer;line-height:0}.api-key-modal-body{margin-top:12px;display:flex;flex-direction:column;gap:10px}.modal-message,.modal-instruction{margin:0;color:#cbd5e1}.modal-message strong{color:#e5e7eb}.api-key-input-group{display:flex;align-items:center;gap:10px}.api-key-input{flex:1 1 auto;background:#0a1020;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:10px 12px}.api-key-input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb33}.api-key-load-btn{background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600}.api-key-load-btn:hover{background:#1d4ed8}.modal-hint{display:flex;align-items:center;gap:8px;color:#94a3b8}.modal-hint a{color:#93c5fd}@media(max-width:1023px){.profile-screen{max-width:640px}.profile-flex{height:400px}.profile-img{height:550px}}@media(max-width:767px){.profile-screen,.profile-flex,.overlay-container{width:100%;max-width:100%;overflow-x:hidden;box-sizing:border-box}.profile-screen{border-radius:0}.profile-flex{height:360px}.profile-img{height:510px}.chips-grid{width:100%;max-width:100%;padding:0 4px;top:50px;bottom:50px;box-sizing:border-box}.chips-column{padding:2px}.chips-column.left{align-items:flex-end}.chips-column.center{align-items:center}.chips-column.right{align-items:flex-start}.frosted-chip{padding:4px 10px;max-width:100%;box-sizing:border-box}.frosted-chip .chip-label{font-size:11px;max-width:100%;overflow:hidden;text-overflow:ellipsis}.view-profile{bottom:14px}}@media(max-width:480px){.profile-flex{height:340px}.profile-img{height:480px}.frosted-chip{padding:3px 8px}.frosted-chip .chip-label{font-size:10px}.header-label{top:14px}.header-label.left{font-size:12px}.header-label.center{font-size:10px}.header-label.right{font-size:12px}}\n"], dependencies: [{ kind: "directive", type: i2_1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2_1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
2546
2457
|
}
|
|
2547
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
2458
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibComponent, decorators: [{
|
|
2548
2459
|
type: Component,
|
|
2549
|
-
args: [{ selector: 'lib-profile-comparison', standalone: false, template: "<div class=\"configure-backend-message\" *ngIf=\"!backendConfigured\">\n Configure backend\n</div>\n\n<div class=\"profile-screen profile-comparison\" *ngIf=\"backendConfigured\"\n [class.fade-all-edges]=\"config.edgeShading !== false\"\n [style.--edge-shading-color]=\"config.shadingColor || 'rgba(44, 40, 47, 0.0)'\"\n [style.--edge-shading-display]=\"config.edgeShading !== false ? 'block' : 'none'\">\n\n <div class=\"profile-comparison__inner\" #shapeContainer>\n <!-- Backgrounds layer: structurally below everything else to fix stacking -->\n <div class=\"profile-comparison__bg-layer\">\n <div class=\"profile-comparison__frame profile-comparison__frame--left bg-frame\"\n [class.is-animating-nudge]=\"leftShapeAnimating\"\n #leftBgFrame>\n <div\n class=\"profile-comparison__bg profile-comparison__bg--left\"\n [ngStyle]=\"{ 'background-image': 'url(' + user1Image + ')', 'transform': user1Transform, 'background-position': user1ObjectPosition }\"\n ></div>\n </div>\n <div class=\"profile-comparison__frame profile-comparison__frame--right bg-frame\"\n [class.is-animating-nudge]=\"rightShapeAnimating\"\n #rightBgFrame>\n <div\n class=\"profile-comparison__bg profile-comparison__bg--right\"\n [ngStyle]=\"{ 'background-image': 'url(' + user2Image + ')', 'transform': user2Transform, 'background-position': user2ObjectPosition }\"\n ></div>\n </div>\n </div>\n\n <!-- Active interactive shapes and content layer -->\n <div class=\"profile-comparison__frame profile-comparison__frame--left swiper-no-swiping\"\n [class.is-animating-nudge]=\"leftShapeAnimating\"\n (click)=\"onShapeClick('left')\"\n #leftFrame>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--glass\"\n viewBox=\"0 0 256 275\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <g filter=\"url(#filter-glass-left)\">\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 L0 268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924\"\n stroke=\"#61C2AB\"\n stroke-opacity=\"0.3\"\n stroke-width=\"0.809707\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n <defs>\n <filter id=\"filter-glass-left\" x=\"-40\" y=\"-40\" width=\"336\" height=\"355\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect1_dropShadow\" result=\"effect2_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect2_dropShadow\" result=\"effect3_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect3_dropShadow\" result=\"effect4_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect4_dropShadow\" result=\"effect5_dropShadow\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect5_dropShadow\" result=\"shape\"/>\n </filter>\n </defs>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--middle\"\n viewBox=\"0 0 256 276\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <clipPath id=\"clip-middle-left\">\n <path d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z\"/>\n </clipPath>\n <filter id=\"filter0_i_2126_3746\" x=\"-0.638672\" y=\"-2\" width=\"256\" height=\"279\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14.2857 0\" result=\"hardAlpha\" />\n <feOffset />\n <feGaussianBlur stdDeviation=\"7.95\" />\n <feComposite in2=\"hardAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" />\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0.516667 0 0 0 0 1 0 0 0 1 0\" />\n <feBlend mode=\"normal\" in2=\"shape\" result=\"effect1_innerShadow_2126_3746\" />\n </filter>\n </defs>\n <g clip-path=\"url(#clip-middle-left)\" filter=\"url(#filter0_i_2126_3746)\">\n <path\n d=\"M245.538 25.2463 L9.36 -1.2459 Q-0.638672 -2.5 -0.638672 7.5 V266.75 Q-0.638672 276.75 9.16 275.25 L246.042 238.623 C251.404 237.794 255.361 233.161 255.361 227.735 V36.2037 C255.361 30.5841 251.126 25.8476 245.538 25.2463 Z\"\n fill=\"#00CFE6\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--left\"\n viewBox=\"0 0 256 278\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <radialGradient id=\"profile-left-fill\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(65.1034 -81.3964 74.955 49.415 128 139)\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#0051E6\" stop-opacity=\"0\" />\n <stop offset=\"0.5\" stop-color=\"#0051E6\" stop-opacity=\"0.035\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </radialGradient>\n <linearGradient id=\"profile-left-stroke\" x1=\"128\" y1=\"0\" x2=\"128\" y2=\"278\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#52FFEB\" />\n <stop offset=\"0.5\" stop-color=\"#41CFA1\" />\n <stop offset=\"1\" stop-color=\"#319990\" />\n </linearGradient>\n </defs>\n <!-- Fill Layer -->\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z\"\n fill=\"url(#profile-left-fill)\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Stroke Layer (Left outer border skipped) -->\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 L0 268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924\"\n fill=\"none\"\n stroke=\"url(#profile-left-stroke)\"\n stroke-width=\"2\"\n stroke-miterlimit=\"3.99393\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Added explicit embedded SVG text anchoring tightly inside the graphical shape bounds instead of pure flexbox logic padding. -->\n <g\n style=\"cursor: pointer !important;\"\n (click)=\"onViewProfile('left'); $event.stopPropagation()\"\n (mousedown)=\"$event.stopPropagation()\"\n >\n <rect [attr.x]=\"viewProfileLeftX - 15\" y=\"240\" width=\"115\" height=\"40\" fill=\"transparent\" pointer-events=\"auto\" />\n <text\n [attr.x]=\"viewProfileLeftX\" y=\"256\"\n fill=\"rgba(255, 255, 255, 0.60)\"\n font-family=\"'Calistoga', serif\"\n font-size=\"9\"\n text-anchor=\"start\"\n [class.is-animating-bob]=\"leftProfileClicked\"\n style=\"text-shadow: 0 2px 4.2px #000;\"\n pointer-events=\"none\"\n >View Profile</text>\n </g>\n </svg>\n <div class=\"profile-comparison__content profile-comparison__content--left\">\n <div class=\"profile-comparison__list profile-comparison__list--left\">\n <!-- Semantic Items only -->\n <ng-container *ngFor=\"let interest of alignedPerson1Interests; let i = index\">\n <div class=\"profile-comparison__item profile-comparison__item--left\">\n <lib-marquee\n class=\"profile-comparison__text\"\n [title]=\"interest\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 100; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n\n <!-- Dash beneath each interest -->\n <div class=\"profile-comparison__dash-container\" [ngSwitch]=\"getDashType(i)\">\n <!-- High Similarity: Short single dash, 0.3 opacity -->\n <svg *ngSwitchCase=\"'high'\" class=\"profile-comparison__dash profile-comparison__dash--high\" width=\"15\" height=\"4\" viewBox=\"0 0 15 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H13\" stroke=\"white\" stroke-width=\"4\" stroke-opacity=\"0.3\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Medium Similarity: Standard 1 dash -->\n <svg *ngSwitchCase=\"'medium'\" class=\"profile-comparison__dash profile-comparison__dash--medium\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Low Similarity: 4 small dashes -->\n <svg *ngSwitchCase=\"'low'\" class=\"profile-comparison__dash profile-comparison__dash--low\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-dasharray=\"2,5\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <div class=\"profile-comparison__frame profile-comparison__frame--right swiper-no-swiping\"\n [class.is-animating-nudge]=\"rightShapeAnimating\"\n (click)=\"onShapeClick('right')\"\n #rightFrame>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--glass\"\n viewBox=\"0 0 257 275\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <g filter=\"url(#filter-glass-right)\">\n <path\n d=\"M256.24 267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 L256.24 267.5\"\n stroke=\"#AB4AFF\"\n stroke-opacity=\"0.3\"\n stroke-width=\"0.809707\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n <defs>\n <filter id=\"filter-glass-right\" x=\"-40\" y=\"-40\" width=\"337\" height=\"355\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect1_dropShadow\" result=\"effect2_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect2_dropShadow\" result=\"effect3_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect3_dropShadow\" result=\"effect4_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect4_dropShadow\" result=\"effect5_dropShadow\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect5_dropShadow\" result=\"shape\"/>\n </filter>\n </defs>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--middle\"\n viewBox=\"0 0 257 276\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <clipPath id=\"clip-middle-right\">\n <path d=\"M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95935 237.368 -9.15527e-05 232.733 -9.15527e-05 227.305 V36.1607 C-9.15527e-05 30.5399 4.23741 25.8028 9.8262 26.4454 Z\"/>\n </clipPath>\n <filter id=\"filter0_i_2126_3750\" x=\"0\" y=\"-2\" width=\"256.24\" height=\"279\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14.2857 0\" result=\"hardAlpha\" />\n <feOffset />\n <feGaussianBlur stdDeviation=\"7.95\" />\n <feComposite in2=\"hardAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" />\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.482353 0 0 0 0 0 0 0 0 0 0.901961 0 0 0 1 0\" />\n <feBlend mode=\"normal\" in2=\"shape\" result=\"effect1_innerShadow_2126_3750\" />\n </filter>\n </defs>\n <g clip-path=\"url(#clip-middle-right)\" filter=\"url(#filter0_i_2126_3750)\">\n <path\n d=\"M9.82611 25.203 L244.066 0.0640762 L246.24 -0.05 Q256.24 -1.2424 256.24 8.75 V266.25 Q256.24 276.258 246.24 274.75 L243.564 274.304 L9.32402 238.195 C3.95935 237.368 -9.15527e-05 232.733 -9.15527e-05 227.305 V36.1607 C-9.15527e-05 30.5399 4.23741 25.8028 9.82611 25.203 Z\"\n fill=\"#7B00E6\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--right\"\n viewBox=\"0 0 257 278\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <radialGradient id=\"profile-right-fill\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(-65.1644 -81.25 -75.0252 49.3261 128.12 138.75)\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#0051E6\" stop-opacity=\"0\" />\n <stop offset=\"0.5\" stop-color=\"#0051E6\" stop-opacity=\"0.035\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </radialGradient>\n <linearGradient id=\"profile-right-stroke\" x1=\"128.12\" y1=\"277.5\" x2=\"128.12\" y2=\"8.50002\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#662C99\" />\n <stop offset=\"0.5\" stop-color=\"#893BD3\" />\n <stop offset=\"1\" stop-color=\"#AB4AFF\" />\n </linearGradient>\n </defs>\n <!-- Fill Layer -->\n <path\n d=\"M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z\"\n fill=\"url(#profile-right-fill)\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Stroke Layer (Right outer border skipped) -->\n <path\n d=\"M256.24 267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 L256.24 267.5\"\n fill=\"none\"\n stroke=\"url(#profile-right-stroke)\"\n stroke-width=\"2\"\n stroke-miterlimit=\"3.99393\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n <g\n style=\"cursor: pointer !important;\"\n (click)=\"onViewProfile('right'); $event.stopPropagation()\"\n (mousedown)=\"$event.stopPropagation()\"\n >\n <rect [attr.x]=\"viewProfileRightX - 100\" y=\"240\" width=\"115\" height=\"40\" fill=\"transparent\" pointer-events=\"auto\" />\n <text\n [attr.x]=\"viewProfileRightX\" y=\"256\"\n fill=\"rgba(255, 255, 255, 0.60)\"\n font-family=\"'Calistoga', serif\"\n font-size=\"9\"\n text-anchor=\"end\"\n [class.is-animating-bob]=\"rightProfileClicked\"\n style=\"text-shadow: 0 2px 4.2px #000;\"\n pointer-events=\"auto\"\n >View Profile</text>\n </g>\n </svg>\n <div class=\"profile-comparison__content profile-comparison__content--right\">\n <div class=\"profile-comparison__list profile-comparison__list--right\">\n <!-- Semantic Items only -->\n <ng-container *ngFor=\"let interest of alignedPerson2Interests; let i = index\">\n <div class=\"profile-comparison__item profile-comparison__item--right\">\n <lib-marquee\n class=\"profile-comparison__text\"\n [title]=\"interest\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 100; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n\n <!-- Dash beneath each interest -->\n <div class=\"profile-comparison__dash-container\" [ngSwitch]=\"getDashType(i)\">\n <!-- High Similarity: Short single dash, 0.5 opacity -->\n <svg *ngSwitchCase=\"'high'\" class=\"profile-comparison__dash profile-comparison__dash--high\" width=\"15\" height=\"4\" viewBox=\"0 0 15 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H13\" stroke=\"white\" stroke-width=\"4\" stroke-opacity=\"0.5\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Medium Similarity: Standard 1 dash -->\n <svg *ngSwitchCase=\"'medium'\" class=\"profile-comparison__dash profile-comparison__dash--medium\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Low Similarity: 4 small dashes -->\n <svg *ngSwitchCase=\"'low'\" class=\"profile-comparison__dash profile-comparison__dash--low\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-dasharray=\"2,5\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <!-- Center text section -->\n <div class=\"profile-comparison__center\" #shapeTextCenter>\n <div class=\"profile-comparison__center-inner\">\n <!-- Exact Items only -->\n <div *ngFor=\"let item of centerItem\" class=\"profile-comparison__center-item\">\n <lib-marquee\n class=\"profile-comparison__center-text\"\n [title]=\"item\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 700; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n <!-- Invisible dash as requested -->\n <div class=\"profile-comparison__center-dash-placeholder\"></div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Loading indicator for alignment process -->\n <div *ngIf=\"isAligning\" class=\"loading-indicator\">\n <div class=\"loading-spinner\"></div>\n </div>\n</div>\n\n\n", styles: ["@keyframes textDropIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes nudgeLeft{0%,to{transform:translate(-24%) scale(.86)}50%{transform:translate(-26%) scale(.86)}}@keyframes nudgeRight{0%,to{transform:translate(24%) scale(.86)}50%{transform:translate(26%) scale(.86)}}.profile-img.left{-webkit-mask-image:linear-gradient(to right,rgb(0,0,0) 0%,rgb(0,0,0) calc(100% - var(--overlap-size, 40px)),rgba(0,0,0,0) 100%);-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-image:linear-gradient(to right,#fff 0% calc(100% - var(--overlap-size, 40px)),#fff0);mask-size:100% 100%;mask-repeat:no-repeat;margin-right:calc(-.5 * var(--overlap-size, 40px))}.profile-img.right{-webkit-mask-image:linear-gradient(to left,rgb(0,0,0) 0%,rgb(0,0,0) calc(100% - var(--overlap-size, 40px)),rgba(0,0,0,0) 100%);-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-image:linear-gradient(to left,#fff 0% calc(100% - var(--overlap-size, 40px)),#fff0);mask-size:100% 100%;mask-repeat:no-repeat;margin-left:calc(-.5 * var(--overlap-size, 40px))}.profile-img.fade-all{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent,black 15%,black 85%,transparent);-webkit-mask-composite:source-in;mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent,black 15%,black 85%,transparent);mask-composite:intersect}.profile-img.fade-all.left{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%)}.profile-img.fade-all.right{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to left,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to left,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%)}.configure-backend-message{padding:1rem;text-align:center;color:#bdc3c7;background:#34495e;border-radius:8px}.profile-screen{width:100%;max-width:460px;margin:0 auto;overflow:visible;position:relative}.profile-flex{display:flex;width:100%;height:auto;align-items:center;overflow:hidden;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);gap:0;margin:0;padding:0;position:relative;box-shadow:none;border:none;--overlap-size: 40px}.profile-flex.fade-all{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 15%,black 85%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);-webkit-mask-composite:intersect;mask-image:linear-gradient(to right,transparent 0%,black 15%,black 85%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);mask-composite:intersect}.shape-bg{position:relative;pointer-events:auto;width:100%;height:100%}.shape-bg1,.shape-bg2{position:absolute;width:75%;max-width:none;height:350px;opacity:1;transition:transform .2s ease-out}.shape-bg1{z-index:1;left:0}.shape-bg2{right:0}.shape-bg.fade-all .shape-bg1{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%)}.shape-bg.fade-all .shape-bg2{-webkit-mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%)}.shape-bg svg{cursor:grab;overflow:visible!important}.shape-bg svg:active{cursor:grabbing}.profile-img{width:50%;height:350px;position:relative;flex-shrink:0;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);margin:0;padding:0;left:0;top:0;border:none;box-sizing:border-box;overflow:hidden;box-shadow:none;outline:none}.profile-img img{transition:transform .3s ease;width:100%;height:100%;object-fit:cover;display:block;object-position:center 30%;opacity:.75;transform-origin:center center;border:none;outline:none;box-shadow:none;position:relative;top:0;left:0;filter:contrast(1.2) brightness(1.1) saturate(1.1);image-rendering:auto}.shape{position:absolute;top:32px;width:100%;left:0;z-index:2;pointer-events:none}.shape-bg{position:relative;pointer-events:auto}.shape-text{position:absolute;top:0;left:0;right:0;width:100%;height:100%;z-index:1;padding:0 1.25rem;box-sizing:border-box;pointer-events:none}.shape-text-left{margin-top:40px;text-align:left;position:absolute;top:0;bottom:0;right:calc(50% + 75px);z-index:2;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-end}.shape-text-left .scroll-container{width:100%}.shape-text-center{position:absolute;left:0;right:0;margin:0 auto;width:fit-content;font-family:Gilroy,sans-serif;font-weight:700;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;text-align:center;display:flex;justify-content:center;align-items:center;flex-direction:column;text-shadow:0 2px 4.2px #000;z-index:10;transition:transform .3s ease;pointer-events:none;top:50%;transform:translateY(-50%);padding:0 .625rem;margin-top:3.8125rem}.shape-text-right{margin-top:40px;text-align:right;position:absolute;top:0;bottom:0;left:calc(50% + 70px);z-index:2;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-start}.shape-text-right .scroll-container{width:100%}.shape-p{font-family:Gilroy,sans-serif;font-weight:400;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;padding-bottom:.125rem;text-align:right;text-shadow:0 2px 4.2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;direction:rtl;-webkit-user-select:none;user-select:none;margin:0}.shape-p-center{font-family:Gilroy,sans-serif;font-weight:700;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;margin:0;text-align:center;display:flex;justify-content:center;align-items:center;text-shadow:0 2px 4.2px #000}.shape-p-right{font-family:Gilroy,sans-serif;font-weight:400;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;padding-bottom:.125rem;text-align:left;text-shadow:0 2px 4.2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0;-webkit-user-select:none;user-select:none}.dash-item{color:#ffffff4d!important;font-size:.625rem!important;height:.625rem}.spacer-item{height:auto!important;color:#fffc!important;font-weight:700;font-size:.75rem!important;letter-spacing:2px}.shape-h2{font-family:Calistoga,serif;font-weight:400;font-size:.625rem;letter-spacing:0%;color:#fff;text-shadow:0 2px 4.2px #000;cursor:pointer;transition:all .3s ease;border:none;outline:none;white-space:nowrap;pointer-events:auto;z-index:100;position:relative;margin-left:.5rem}.shape-h2:hover{opacity:.8;transform:translateY(-2px)}.shape-h2-right{font-family:Calistoga,serif;font-weight:400;font-size:.625rem;letter-spacing:0%;color:#fff;text-shadow:0 2px 4.2px #000;cursor:pointer;transition:all .3s ease;border:none;outline:none;white-space:nowrap;pointer-events:auto;z-index:100;position:relative;margin-right:3.75rem}.shape-h2-right:hover{opacity:.8;transform:translateY(-2px)}.scroll-when-long{display:inline-block;max-width:4.5625rem;white-space:nowrap;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}.scroll-when-long::-webkit-scrollbar{height:2px}.scroll-when-long::-webkit-scrollbar-track{background:#eee;border-radius:.625rem}.scroll-when-long::-webkit-scrollbar-thumb{background:#888;border-radius:.625rem}.scroll-when-long::-webkit-scrollbar-thumb:hover{background:#555}.shape-text p{cursor:default;-webkit-user-select:none;user-select:none}.shape-text p:active{cursor:default}:host{display:block}.profile-comparison{width:100%;padding:0;box-sizing:border-box;display:flex;justify-content:center;align-items:center;overflow:visible}.profile-comparison__inner{position:relative;width:100%;min-width:25.75rem;max-width:100%;min-height:500px;display:grid;grid-template-columns:1fr;grid-template-rows:auto;background:transparent;overflow:visible}.profile-comparison__bg-layer{grid-area:1/1;position:relative;width:100%;height:100%;min-height:inherit;pointer-events:none;display:grid;grid-template-columns:1fr;grid-template-rows:1fr}.profile-comparison__frame{grid-area:1/1;position:relative;pointer-events:none}.profile-comparison__frame.bg-frame{width:100%;height:100%;min-height:inherit;overflow:hidden;border-radius:10px;-webkit-mask-composite:source-in;mask-composite:intersect;will-change:auto}.profile-comparison__frame.bg-frame.profile-comparison__frame--left{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);-webkit-mask-size:100% 100%,100% 100%;mask-size:100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.fade-all-edges .profile-comparison__frame.bg-frame.profile-comparison__frame--left.edge-at-left-border{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to right,transparent 0%,black 20px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to right,transparent 0%,black 20px,black 100%);-webkit-mask-size:100% 100%,100% 100%,100% 100%;mask-size:100% 100%,100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__frame.bg-frame.profile-comparison__frame--right{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);-webkit-mask-size:100% 100%,100% 100%;mask-size:100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.fade-all-edges .profile-comparison__frame.bg-frame.profile-comparison__frame--right.edge-at-right-border{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to left,transparent 0%,black 20px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to left,transparent 0%,black 20px,black 100%);-webkit-mask-size:100% 100%,100% 100%,100% 100%;mask-size:100% 100%,100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__frame--left{transform:translate(-24%) scale(.86)}.profile-comparison__frame--left.is-animating-nudge{animation:nudgeLeft .5s ease-out}.profile-comparison__frame--left .profile-comparison__frame-svg{-webkit-mask-image:var(--edge-mask-left, linear-gradient(to right, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)));mask-image:var(--edge-mask-left, linear-gradient(to right, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)))}.profile-comparison__frame--left .profile-comparison__frame-svg--glass{transform:scale(1.01) translateY(-2px);transform-origin:center;-webkit-mask-image:none;mask-image:none}.profile-comparison__frame--right{transform:translate(24%) scale(.86)}.profile-comparison__frame--right.is-animating-nudge{animation:nudgeRight .5s ease-out}.profile-comparison__frame--right .profile-comparison__frame-svg{-webkit-mask-image:var(--edge-mask-right, linear-gradient(to left, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)));mask-image:var(--edge-mask-right, linear-gradient(to left, transparent, black var(--edge-shading-width, 28px)))}.profile-comparison__frame--right .profile-comparison__frame-svg--glass{transform:scale(1.01) translate(-1px,-2px);transform-origin:center;-webkit-mask-image:none;mask-image:none}.profile-comparison__frame--right .profile-comparison__frame-svg--bottom{width:113.2%}.profile-comparison__frame-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:auto;overflow:visible}.profile-comparison__frame-svg--glass{z-index:1}.profile-comparison__frame-svg--middle{z-index:2;mix-blend-mode:screen}.profile-comparison__frame-svg--left,.profile-comparison__frame-svg--right{z-index:3}.profile-comparison__frame-svg--bottom{width:113.7%;height:112.6%;inset:51% auto auto 50%;transform:translate(-50%,-51%)}.profile-comparison__bg{position:absolute;top:0;left:0;right:0;height:130%;background-size:cover;background-repeat:no-repeat;background-position:center 30%;pointer-events:auto;opacity:.8;transition:filter .6s ease,opacity .6s ease}.profile-comparison__bg--left{background-position:right center;margin-right:0%;-webkit-mask-image:linear-gradient(to right,black 50%,transparent 100%);mask-image:linear-gradient(to right,black 50%,transparent 100%);-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__bg--right{background-position:left center;margin-left:0%;-webkit-mask-image:linear-gradient(to left,black 50%,transparent 100%);mask-image:linear-gradient(to left,black 50%,transparent 100%);-webkit-mask-composite:source-in;mask-composite:intersect}@keyframes viewProfileBob{0%{transform:translateY(0)}50%{transform:translateY(-3px)}to{transform:translateY(0)}}.profile-comparison svg text{transition:opacity .3s ease}.profile-comparison svg text.is-animating-bob{animation:viewProfileBob .6s ease-in-out;fill:#fff!important}.profile-comparison__content{position:relative;z-index:20;padding:50px 12% 75px;min-height:100%;display:flex;flex-direction:column;justify-content:flex-start;color:#ecf0f1;font-family:Gilroy,serif;pointer-events:none}.profile-comparison__content--left{align-items:flex-end;text-align:right;padding-right:29%}.profile-comparison__content--right{align-items:flex-start;text-align:left;padding-left:29%}.profile-comparison__list{display:flex;flex-direction:column;gap:2px;width:100%;pointer-events:auto}.profile-comparison__list--left{align-items:flex-end;text-align:right;padding-right:29%}.profile-comparison__list--right{align-items:flex-start;text-align:left;padding-left:29%}.profile-comparison__item{display:flex;flex-direction:column;height:auto;width:11rem;flex-shrink:0;gap:6px;margin-bottom:6px;animation:textDropIn .4s ease-out backwards}.profile-comparison__item--left{align-items:flex-end;text-align:right}.profile-comparison__item--right{align-items:flex-start;text-align:left}.profile-comparison__text{font-family:Gilroy,sans-serif;font-size:15px;font-weight:100!important;color:#fff;width:100%;display:block;text-shadow:0 2px 4.2px #000}.profile-comparison__text ::ng-deep *{font-weight:400!important;text-shadow:0 2px 4.2px #000}.profile-comparison__dash-container{display:flex;width:100%;height:8px;align-items:center}.profile-comparison__item--left .profile-comparison__dash-container{justify-content:flex-end}.profile-comparison__item--right .profile-comparison__dash-container{justify-content:flex-start}.profile-comparison__dash{display:block}.profile-comparison__center-empty,.profile-comparison__center-dash-placeholder{height:8px;width:100%}.profile-comparison__center{grid-area:1/1;display:flex;align-items:flex-start;justify-content:center;z-index:15;pointer-events:none;transform:scale(.86)}.profile-comparison__center-inner{display:flex;flex-direction:column;align-items:center;text-align:center;justify-content:flex-start;padding:50px 0 75px;gap:6px;width:100%}.profile-comparison__center-item{height:auto;min-height:22px;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;width:9rem;flex-shrink:0;gap:6px;margin-bottom:6px;animation:textDropIn .4s ease-out backwards}.profile-comparison__center-text{font-family:Gilroy,sans-serif;font-size:15px;font-weight:700;color:#fff;width:100%;display:block;text-shadow:0 2px 4.2px #000}.profile-comparison__center-text--empty{color:#ecf0f180}.loading-indicator{position:fixed;top:25%;left:50%;transform:translate(-50%,-50%);background:transparent;color:#fff;padding:20px 30px;border-radius:10px;text-align:center;z-index:1000;display:flex;flex-direction:column;align-items:center;gap:8px}.loading-spinner{width:40px;height:40px;border:4px solid rgba(255,255,255,.3);border-top:4px solid #667eea;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-indicator p{margin:0;font-size:16px;font-weight:500}.api-key-modal-overlay{position:fixed;inset:0;background:#0f172a99;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:2000}.api-key-modal{width:min(640px,92vw);background:#0f172a;color:#e2e8f0;border:1px solid #334155;border-radius:12px;box-shadow:0 20px 50px #00000073;padding:16px 18px}.api-key-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.api-key-modal-header h2{margin:0;font-size:20px;font-weight:700;color:#e2e8f0}.warning-icon{color:#fbbf24}.api-key-modal .close-btn{background:#0b1220;border:1px solid #334155;color:#94a3b8;border-radius:8px;padding:6px;cursor:pointer;line-height:0}.api-key-modal-body{margin-top:12px;display:flex;flex-direction:column;gap:10px}.modal-message,.modal-instruction{margin:0;color:#cbd5e1}.modal-message strong{color:#e5e7eb}.api-key-input-group{display:flex;align-items:center;gap:10px}.api-key-input{flex:1 1 auto;background:#0a1020;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:10px 12px}.api-key-input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb33}.api-key-load-btn{background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600}.api-key-load-btn:hover{background:#1d4ed8}.modal-hint{display:flex;align-items:center;gap:8px;color:#94a3b8}.modal-hint a{color:#93c5fd}\n"] }]
|
|
2460
|
+
args: [{ selector: 'lib-profile-comparison', standalone: false, template: "<div class=\"configure-backend-message\" *ngIf=\"!backendConfigured\">\n Configure backend\n</div>\n\n<div class=\"profile-screen\" *ngIf=\"backendConfigured\">\n <div #profileFlex class=\"profile-flex\" [class.fade-all]=\"fadeAllEdges\">\n <svg class=\"image-noise-overlay\" style=\"position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: hidden; z-index: 0;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 320\" fill=\"none\" preserveAspectRatio=\"none\">\n <g filter=\"url(#filter0_n_1_1753)\">\n <rect width=\"100%\" height=\"100%\" fill=\"url(#paint0_radial_1_1753)\" fill-opacity=\"0.33\"/>\n </g>\n <defs>\n <filter id=\"filter0_n_1_1753\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\" filterUnits=\"objectBoundingBox\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\"/>\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.10869565606117249 0.10869565606117249\" stitchTiles=\"stitch\" numOctaves=\"3\" result=\"noise\" seed=\"7339\"/>\n <feColorMatrix in=\"noise\" type=\"luminanceToAlpha\" result=\"alphaNoise\"/>\n <feComponentTransfer in=\"alphaNoise\" result=\"coloredNoise1\">\n <feFuncA type=\"discrete\" tableValues=\"0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \"/>\n </feComponentTransfer>\n <feComposite operator=\"in\" in2=\"shape\" in=\"coloredNoise1\" result=\"noise1Clipped\"/>\n <feFlood flood-color=\"rgba(0, 0, 0, 0.25)\" result=\"color1Flood\"/>\n <feComposite operator=\"in\" in2=\"noise1Clipped\" in=\"color1Flood\" result=\"color1\"/>\n <feMerge result=\"effect1_noise_1_1753\">\n <feMergeNode in=\"shape\"/>\n <feMergeNode in=\"color1\"/>\n </feMerge>\n </filter>\n <radialGradient id=\"paint0_radial_1_1753\" cx=\"50%\" cy=\"50%\" r=\"50%\" fx=\"50%\" fy=\"50%\">\n <stop offset=\"0%\" stop-opacity=\"0\"/>\n <stop offset=\"100%\" stop-opacity=\"0.25\"/>\n </radialGradient>\n </defs>\n </svg>\n <div #profileImgLeft class=\"profile-img left\" [class.fade-all]=\"fadeAllEdges\">\n <img\n [src]=\"user1Image\"\n alt=\"User 1\"\n [style.transform]=\"user1Transform\"\n [style.top]=\"user1Top\"\n [style.height]=\"user1Height\"\n [style.object-position]=\"user1ObjectPosition\"\n (load)=\"onUserImageLoad(1, $event)\"\n />\n </div>\n <div #profileImgRight class=\"profile-img right\" [class.fade-all]=\"fadeAllEdges\">\n <img\n [src]=\"user2Image\"\n alt=\"User 2\"\n [style.transform]=\"user2Transform\"\n [style.top]=\"user2Top\"\n [style.height]=\"user2Height\"\n [style.object-position]=\"user2ObjectPosition\"\n (load)=\"onUserImageLoad(2, $event)\"\n />\n </div>\n <div class=\"image-shade-overlay\"></div>\n\n <!-- Frosted Glass Overlay Layer -->\n <div class=\"overlay-container\">\n <!-- Top Headers -->\n <div class=\"header-label left\">{{ person1Name }}</div>\n <div class=\"header-label center\">Shared</div>\n <div class=\"header-label right\">{{ person2Name }}</div>\n\n <!-- Chips Columns Grid -->\n <div class=\"chips-grid\">\n <!-- Person 1 Unique Interests -->\n <div class=\"chips-column left\">\n <ng-container *ngFor=\"let interest of getPerson1UniqueInterests(); let i = index\">\n <div class=\"frosted-chip person-a\" [style.margin-right.px]=\"getChipOffset(i, 'left')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Shared Interests (Center Column) -->\n <div class=\"chips-column center\">\n <ng-container *ngFor=\"let interest of centerItem; let i = index\">\n <div class=\"frosted-chip shared\" *ngIf=\"interest && interest !== '-' && interest !== '----'\" [style.margin-left.px]=\"getChipOffset(i, 'center')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Person 2 Unique Interests -->\n <div class=\"chips-column right\">\n <ng-container *ngFor=\"let interest of getPerson2UniqueInterests(); let i = index\">\n <div class=\"frosted-chip person-b\" [style.margin-left.px]=\"getChipOffset(i, 'right')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Bottom Actions -->\n <div class=\"view-profile left\" (click)=\"onViewProfile('left')\">View Profile</div>\n <div class=\"view-profile right\" (click)=\"onViewProfile('right')\">View Profile</div>\n </div>\n\n <!-- Loading indicator for alignment process -->\n <div *ngIf=\"isAligning\" class=\"loading-indicator\">\n <div class=\"loading-spinner\"></div>\n </div>\n</div>\n\n\n", styles: [":host{display:block;width:100%;max-width:100%;overflow-x:hidden!important;box-sizing:border-box!important}.profile-img.left{position:absolute;top:0;bottom:0;left:0;width:58%;height:100%;margin:0;z-index:1;-webkit-mask-image:linear-gradient(to right,black 0%,black 45%,transparent 92%);mask-image:linear-gradient(to right,black 0%,black 45%,transparent 92%)}.profile-img.right{position:absolute;inset:0 0 0 auto;width:58%;height:100%;margin:0;z-index:2;-webkit-mask-image:linear-gradient(to right,transparent 8%,black 55%,black 100%);mask-image:linear-gradient(to right,transparent 8%,black 55%,black 100%)}@media(min-width:768px){.profile-img.left{width:65%;-webkit-mask-image:linear-gradient(to right,black 0%,black 40%,transparent 90%);mask-image:linear-gradient(to right,black 0%,black 40%,transparent 90%)}.profile-img.right{width:65%;-webkit-mask-image:linear-gradient(to right,transparent 10%,black 60%,black 100%);mask-image:linear-gradient(to right,transparent 10%,black 60%,black 100%)}}.configure-backend-message{padding:1rem;text-align:center;color:#bdc3c7;background:#34495e;border-radius:8px}.backend-error-message{padding:.75rem 1rem;text-align:center;color:#fef3c7;background:#b4530940;border:1px solid rgba(245,158,11,.5);border-radius:8px;margin-bottom:.5rem}.profile-screen{width:100%;max-width:768px;margin:0 auto;overflow:hidden;position:relative;border-radius:12px}.profile-flex{display:flex;width:100%;height:440px;overflow:hidden;background:transparent;gap:0;margin:0;padding:0;position:relative;box-shadow:none;border:none;--overlap-size: 40px}.profile-flex.fade-all{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 18%,black 82%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);-webkit-mask-composite:intersect;mask-image:linear-gradient(to right,transparent 0%,black 18%,black 82%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);mask-composite:intersect}.profile-flex .image-shade-overlay{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:3;background:radial-gradient(ellipse at center,transparent 40%,rgba(41,37,45,.25) 100%)}.profile-flex .image-noise-overlay{position:absolute;inset:0;width:100%;height:100%;max-width:100%;max-height:100%;pointer-events:none;overflow:hidden;z-index:0}.shape-bg1,.shape-bg2{position:absolute;max-width:250px;opacity:1;transition:transform .2s ease-out}.shape-bg1{z-index:1;left:0}.shape-bg2{right:0}.shape-bg.fade-all .shape-bg1{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%)}.shape-bg.fade-all .shape-bg2{-webkit-mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%)}.shape-bg svg{cursor:grab}.shape-bg svg:active{cursor:grabbing}.profile-img{height:100%;position:relative;flex-shrink:0;background:transparent;margin:0;padding:0;left:0;top:0;border:none;box-sizing:border-box;overflow:hidden;box-shadow:none;outline:none}.profile-img img{transition:transform .3s ease;width:100%;height:100%;object-fit:cover;display:block;object-position:center center;opacity:1;transform-origin:center center;border:none;outline:none;box-shadow:none;position:relative;top:0;left:0;filter:contrast(1.2) brightness(1.1) saturate(1.1);image-rendering:auto}.overlay-container{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:10;-webkit-user-select:none;user-select:none;overflow:hidden;background:#29252da6;backdrop-filter:blur(.5px);-webkit-backdrop-filter:blur(.5px)}.header-label{position:absolute;top:20px;font-family:Gilroy,sans-serif;text-shadow:0 1px 8px rgba(0,0,0,.8);pointer-events:none}.header-label.left{left:5%;transform:none;font-size:14px;font-weight:400;color:#fffffff2}.header-label.center{left:50%;transform:translate(-50%);font-size:12px;font-weight:400;color:#fffc;letter-spacing:0}.header-label.right{right:5%;left:auto;transform:none;font-size:14px;font-weight:400;color:#fffffff2}.chips-grid{position:absolute;inset:45px 0;display:grid;grid-template-columns:repeat(3,1fr);padding:0;pointer-events:none;box-sizing:border-box}.chips-column{display:flex;flex-direction:column;gap:8px;max-height:100%;overflow-y:auto;padding:4px 10px;box-sizing:border-box;width:100%;scrollbar-width:none}.chips-column::-webkit-scrollbar{display:none}.chips-column.left{align-items:flex-end}.chips-column.center{align-items:center}.chips-column.right{align-items:flex-start}.frosted-chip{position:relative;cursor:pointer;pointer-events:auto;transition:transform .2s ease,box-shadow .2s ease,background .2s ease;flex-shrink:0;padding:6px 18px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;border:none;background:#ffffff30;box-shadow:inset 0 1.5px 2px #ffffff59;backdrop-filter:blur(.5px);-webkit-backdrop-filter:blur(.5px)}.frosted-chip:hover{transform:scale(1.04);background:#ffffff29}.frosted-chip .chip-label{position:relative;color:#a8a8a8;text-align:center;font-family:Gilroy,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:140%;letter-spacing:-.01em;text-shadow:none;white-space:nowrap;pointer-events:none;z-index:1}.view-profile{position:absolute;bottom:40px!important;color:#ffffff80;text-shadow:0 2px 4.2px #000;font-family:Calistoga,serif;font-size:18px;font-style:normal;font-weight:400;line-height:normal;cursor:pointer;pointer-events:auto;transition:opacity .2s ease,transform .2s ease}.view-profile:hover{opacity:.85}.view-profile.left{left:5%;transform:none}.view-profile.left:hover{transform:translateY(-2px)}.view-profile.right{right:5%;left:auto;transform:none}.view-profile.right:hover{transform:translateY(-2px)}#drag-preview{position:absolute;pointer-events:none;display:none;background:#000000b3;color:#fff;padding:5px 10px;border-radius:5px;z-index:9999;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shape-text-container{width:70px;position:relative;overflow:hidden}.draggable{width:70px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:grab;-webkit-user-select:none;user-select:none;position:relative;display:inline-block}.dragging{width:auto;text-overflow:clip;cursor:grabbing;position:absolute;z-index:1000}.p-wrapper{width:70px;overflow-x:hidden}.shape-text-center{display:flex;justify-content:space-between;flex-direction:column;gap:10px;height:100%;margin-top:55px}.loading-indicator{position:absolute;inset:0;width:100%;height:100%;background:#00000040;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);color:#fff;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;pointer-events:auto}.loading-spinner{width:44px;height:44px;border:3.5px solid rgba(255,255,255,.25);border-top:3.5px solid #ffffff;border-radius:50%;animation:spin .8s linear infinite;box-shadow:0 0 15px #fff3}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-indicator p{margin:0;font-size:14px;font-weight:500;color:#ffffffd9;letter-spacing:.02em}.api-key-modal-overlay{position:fixed;inset:0;background:#0f172a99;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:2000}.api-key-modal{width:min(640px,92vw);background:#0f172a;color:#e2e8f0;border:1px solid #334155;border-radius:12px;box-shadow:0 20px 50px #00000073;padding:16px 18px}.api-key-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.api-key-modal-header h2{margin:0;font-size:20px;font-weight:700;color:#e2e8f0}.warning-icon{color:#fbbf24}.api-key-modal .close-btn{background:#0b1220;border:1px solid #334155;color:#94a3b8;border-radius:8px;padding:6px;cursor:pointer;line-height:0}.api-key-modal-body{margin-top:12px;display:flex;flex-direction:column;gap:10px}.modal-message,.modal-instruction{margin:0;color:#cbd5e1}.modal-message strong{color:#e5e7eb}.api-key-input-group{display:flex;align-items:center;gap:10px}.api-key-input{flex:1 1 auto;background:#0a1020;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:10px 12px}.api-key-input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb33}.api-key-load-btn{background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600}.api-key-load-btn:hover{background:#1d4ed8}.modal-hint{display:flex;align-items:center;gap:8px;color:#94a3b8}.modal-hint a{color:#93c5fd}@media(max-width:1023px){.profile-screen{max-width:640px}.profile-flex{height:400px}.profile-img{height:550px}}@media(max-width:767px){.profile-screen,.profile-flex,.overlay-container{width:100%;max-width:100%;overflow-x:hidden;box-sizing:border-box}.profile-screen{border-radius:0}.profile-flex{height:360px}.profile-img{height:510px}.chips-grid{width:100%;max-width:100%;padding:0 4px;top:50px;bottom:50px;box-sizing:border-box}.chips-column{padding:2px}.chips-column.left{align-items:flex-end}.chips-column.center{align-items:center}.chips-column.right{align-items:flex-start}.frosted-chip{padding:4px 10px;max-width:100%;box-sizing:border-box}.frosted-chip .chip-label{font-size:11px;max-width:100%;overflow:hidden;text-overflow:ellipsis}.view-profile{bottom:14px}}@media(max-width:480px){.profile-flex{height:340px}.profile-img{height:480px}.frosted-chip{padding:3px 8px}.frosted-chip .chip-label{font-size:10px}.header-label{top:14px}.header-label.left{font-size:12px}.header-label.center{font-size:10px}.header-label.right{font-size:12px}}\n"] }]
|
|
2550
2461
|
}], ctorParameters: () => [{ type: ProfileComparisonBackendService }, { type: i0.Renderer2 }, { type: FileConversionService }, { type: ImageCompressionService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
|
|
2551
2462
|
type: Optional
|
|
2552
2463
|
}, {
|
|
@@ -2560,59 +2471,68 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
2560
2471
|
type: Input
|
|
2561
2472
|
}], fadeAllEdges: [{
|
|
2562
2473
|
type: Input
|
|
2474
|
+
}], person1Name: [{
|
|
2475
|
+
type: Input
|
|
2476
|
+
}], person2Name: [{
|
|
2477
|
+
type: Input
|
|
2563
2478
|
}], matrixDataChange: [{
|
|
2564
2479
|
type: Output
|
|
2565
2480
|
}], rawLLMOutputChange: [{
|
|
2566
2481
|
type: Output
|
|
2567
2482
|
}], viewProfileClick: [{
|
|
2568
2483
|
type: Output
|
|
2569
|
-
}], edgeDrag: [{
|
|
2570
|
-
type: Output
|
|
2571
|
-
}], onResize: [{
|
|
2572
|
-
type: HostListener,
|
|
2573
|
-
args: ['window:resize']
|
|
2574
2484
|
}], leftContainer: [{
|
|
2575
2485
|
type: ViewChild,
|
|
2576
2486
|
args: ['leftContainer']
|
|
2577
2487
|
}], rightContainer: [{
|
|
2578
2488
|
type: ViewChild,
|
|
2579
2489
|
args: ['rightContainer']
|
|
2580
|
-
}],
|
|
2581
|
-
type: ViewChild,
|
|
2582
|
-
args: ['leftFrame']
|
|
2583
|
-
}], rightFrame: [{
|
|
2490
|
+
}], profileFlex: [{
|
|
2584
2491
|
type: ViewChild,
|
|
2585
|
-
args: ['
|
|
2586
|
-
}],
|
|
2492
|
+
args: ['profileFlex']
|
|
2493
|
+
}], profileImgLeft: [{
|
|
2587
2494
|
type: ViewChild,
|
|
2588
|
-
args: ['
|
|
2589
|
-
}],
|
|
2495
|
+
args: ['profileImgLeft']
|
|
2496
|
+
}], profileImgRight: [{
|
|
2590
2497
|
type: ViewChild,
|
|
2591
|
-
args: ['
|
|
2498
|
+
args: ['profileImgRight']
|
|
2592
2499
|
}], shapeContainer: [{
|
|
2593
2500
|
type: ViewChild,
|
|
2594
2501
|
args: ['shapeContainer']
|
|
2502
|
+
}], shapeBg: [{
|
|
2503
|
+
type: ViewChild,
|
|
2504
|
+
args: ['shapeBg']
|
|
2505
|
+
}], shapeBg1: [{
|
|
2506
|
+
type: ViewChild,
|
|
2507
|
+
args: ['shapeBg1']
|
|
2508
|
+
}], shapeBg2: [{
|
|
2509
|
+
type: ViewChild,
|
|
2510
|
+
args: ['shapeBg2']
|
|
2511
|
+
}], shapeTextLeft: [{
|
|
2512
|
+
type: ViewChild,
|
|
2513
|
+
args: ['shapeTextLeft']
|
|
2514
|
+
}], shapeTextRight: [{
|
|
2515
|
+
type: ViewChild,
|
|
2516
|
+
args: ['shapeTextRight']
|
|
2595
2517
|
}], shapeTextCenter: [{
|
|
2596
2518
|
type: ViewChild,
|
|
2597
2519
|
args: ['shapeTextCenter']
|
|
2598
2520
|
}] } });
|
|
2599
2521
|
|
|
2600
2522
|
class ProfileComparisonLibModule {
|
|
2601
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2602
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.
|
|
2523
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2524
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibModule, declarations: [ProfileComparisonLibComponent], imports: [CommonModule,
|
|
2603
2525
|
HttpClientModule,
|
|
2604
2526
|
FormsModule,
|
|
2605
|
-
ReactiveFormsModule,
|
|
2606
|
-
|
|
2607
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibModule, providers: [
|
|
2527
|
+
ReactiveFormsModule], exports: [ProfileComparisonLibComponent] });
|
|
2528
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibModule, providers: [
|
|
2608
2529
|
ProfileComparisonBackendService
|
|
2609
2530
|
], imports: [CommonModule,
|
|
2610
2531
|
HttpClientModule,
|
|
2611
2532
|
FormsModule,
|
|
2612
|
-
ReactiveFormsModule
|
|
2613
|
-
LibMarqueeModule] });
|
|
2533
|
+
ReactiveFormsModule] });
|
|
2614
2534
|
}
|
|
2615
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
2535
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibModule, decorators: [{
|
|
2616
2536
|
type: NgModule,
|
|
2617
2537
|
args: [{
|
|
2618
2538
|
declarations: [
|
|
@@ -2622,8 +2542,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
2622
2542
|
CommonModule,
|
|
2623
2543
|
HttpClientModule,
|
|
2624
2544
|
FormsModule,
|
|
2625
|
-
ReactiveFormsModule
|
|
2626
|
-
LibMarqueeModule
|
|
2545
|
+
ReactiveFormsModule
|
|
2627
2546
|
],
|
|
2628
2547
|
exports: [
|
|
2629
2548
|
ProfileComparisonLibComponent
|