@naniteninja/profile-comparison-lib 0.0.1 → 1.0.0
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.
- package/README.md +193 -314
- package/fesm2022/naniteninja-profile-comparison-lib.mjs +1061 -1256
- package/fesm2022/naniteninja-profile-comparison-lib.mjs.map +1 -1
- package/index.d.ts +168 -210
- package/package.json +3 -19
package/README.md
CHANGED
|
@@ -1,314 +1,193 @@
|
|
|
1
|
-
# Profile Comparison Library
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
constructor(private profileService: ProfileService) {}
|
|
195
|
-
|
|
196
|
-
detectFaces(imageFile: File) {
|
|
197
|
-
this.profileService.detectFace(imageFile, environment.apiNinjasKey)
|
|
198
|
-
.subscribe(result => {
|
|
199
|
-
console.log(`Found ${result.faces.length} faces`);
|
|
200
|
-
result.faces.forEach((face, index) => {
|
|
201
|
-
console.log(`Face ${index + 1}: x=${face.x}, y=${face.y}, width=${face.width}, height=${face.height}`);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### Complete Profile Comparison
|
|
208
|
-
|
|
209
|
-
```typescript
|
|
210
|
-
import { EmbeddingService, ProfileService, IProfileConfig } from "profile-comparison-lib-test";
|
|
211
|
-
import { environment } from "../environments/environment";
|
|
212
|
-
|
|
213
|
-
export class ProfileComparisonComponent {
|
|
214
|
-
constructor(private embeddingService: EmbeddingService, private profileService: ProfileService) {}
|
|
215
|
-
|
|
216
|
-
compareProfiles(config: IProfileConfig) {
|
|
217
|
-
// Compare interests between person1 and person2
|
|
218
|
-
this.embeddingService.findBestMatchingPairs(
|
|
219
|
-
config.person1Interests,
|
|
220
|
-
config.person2Interests,
|
|
221
|
-
0.15
|
|
222
|
-
).subscribe(matches => {
|
|
223
|
-
console.log("Interest matches:", matches);
|
|
224
|
-
|
|
225
|
-
// Sort by similarity score
|
|
226
|
-
const sortedMatches = matches.sort((a, b) => b.score - a.score);
|
|
227
|
-
|
|
228
|
-
// Display top matches
|
|
229
|
-
sortedMatches.slice(0, 5).forEach((match) => {
|
|
230
|
-
console.log(`${match.wordA} matches ${match.wordB} (${(match.score * 100).toFixed(1)}%)`);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
## Component Usage
|
|
238
|
-
|
|
239
|
-
The library includes a basic component that can be used as a starting point:
|
|
240
|
-
|
|
241
|
-
```html
|
|
242
|
-
<lib-profile-comparison-lib></lib-profile-comparison-lib>
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
## Building the Library
|
|
246
|
-
|
|
247
|
-
```bash
|
|
248
|
-
ng build profile-comparison-lib-test
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
## Running Tests
|
|
252
|
-
|
|
253
|
-
```bash
|
|
254
|
-
ng test profile-comparison-lib-test
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
## API Keys Required
|
|
258
|
-
|
|
259
|
-
This library requires an API key from [API Ninjas](https://api.api-ninjas.com/) for:
|
|
260
|
-
|
|
261
|
-
- Text similarity comparison
|
|
262
|
-
- Face detection functionality
|
|
263
|
-
|
|
264
|
-
Sign up at API Ninjas to get your free API key.
|
|
265
|
-
|
|
266
|
-
## Browser Compatibility
|
|
267
|
-
|
|
268
|
-
- Chrome 119+
|
|
269
|
-
- Firefox 115+
|
|
270
|
-
- Safari 16.4+
|
|
271
|
-
- Edge 119+
|
|
272
|
-
|
|
273
|
-
Angular 20 requires modern browsers with ES2022+ support. TensorFlow.js requires WebGL support for optimal performance.
|
|
274
|
-
|
|
275
|
-
## Performance Considerations
|
|
276
|
-
|
|
277
|
-
- The Universal Sentence Encoder model (~25MB) is loaded lazily on first use
|
|
278
|
-
- Model loading is cached for subsequent operations
|
|
279
|
-
- Face detection API calls count against your API Ninjas quota
|
|
280
|
-
- Consider implementing caching for repeated similarity calculations
|
|
281
|
-
|
|
282
|
-
## Error Handling
|
|
283
|
-
|
|
284
|
-
All service methods return observables with proper error handling. Wrap calls in try-catch blocks or use RxJS error operators:
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
this.embeddingService
|
|
288
|
-
.calculateSimilarity('text1', 'text2')
|
|
289
|
-
.pipe(
|
|
290
|
-
catchError((error) => {
|
|
291
|
-
console.error("Similarity calculation failed:", error);
|
|
292
|
-
return of(0); // Return default similarity
|
|
293
|
-
})
|
|
294
|
-
)
|
|
295
|
-
.subscribe((similarity) => {
|
|
296
|
-
// Handle successful result
|
|
297
|
-
});
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
## Contributing
|
|
301
|
-
|
|
302
|
-
1. Fork the repository
|
|
303
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
304
|
-
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
305
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
306
|
-
5. Open a Pull Request
|
|
307
|
-
|
|
308
|
-
## License
|
|
309
|
-
|
|
310
|
-
This project is licensed under the MIT License.
|
|
311
|
-
|
|
312
|
-
## Support
|
|
313
|
-
|
|
314
|
-
For issues and questions, please create an issue in the GitHub repository.
|
|
1
|
+
# Profile Comparison Library
|
|
2
|
+
|
|
3
|
+
Angular library that renders a side-by-side profile comparison (two profile images, a central draggable shape, and aligned interest lists). It is a **thin client**: you provide a backend URL; the library sends user config to your backend and displays the payload it returns. No API keys or key-entry modals in the library.
|
|
4
|
+
|
|
5
|
+
**You need a backend** that implements the profile comparison API (e.g. the **profile-comparison-server** repo). Provide its base URL via `PROFILE_COMPARISON_API_BASE_URL`. If not provided, the component shows **"Configure backend"**.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
1. **Install the package**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install profile-comparison-lib
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
2. **Provide the backend URL**
|
|
18
|
+
|
|
19
|
+
In `app.config.ts` (or `AppModule`):
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { PROFILE_COMPARISON_API_BASE_URL } from 'profile-comparison-lib';
|
|
23
|
+
|
|
24
|
+
providers: [
|
|
25
|
+
{ provide: PROFILE_COMPARISON_API_BASE_URL, useValue: 'https://your-backend.com/api' },
|
|
26
|
+
]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or with a factory (e.g. runtime toggle between backends):
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
providers: [{
|
|
33
|
+
provide: PROFILE_COMPARISON_API_BASE_URL,
|
|
34
|
+
useFactory: (backendUrl: BackendUrlService) => () => backendUrl.getBaseUrl(),
|
|
35
|
+
deps: [BackendUrlService],
|
|
36
|
+
}]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
3. **Use the component**
|
|
40
|
+
|
|
41
|
+
Import the module and add the component to a template:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { ProfileComparisonLibModule } from 'profile-comparison-lib';
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<lib-profile-comparison [config]="config"></lib-profile-comparison>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## What the element looks like
|
|
54
|
+
|
|
55
|
+
When you use `<lib-profile-comparison>` in another Angular app you get:
|
|
56
|
+
|
|
57
|
+
- **Side-by-side profile comparison**: two profile images (left and right) with a central draggable “shape” and aligned interest lists between them.
|
|
58
|
+
- **Main regions**: left image, right image, center shape with text, loading indicator when a request is in progress.
|
|
59
|
+
- **Action**: “View Profile” on each side; the component emits `viewProfileClick` with `{ side: 'left' | 'right' }` so the host can route or act.
|
|
60
|
+
|
|
61
|
+
No key-entry modals or API key inputs appear in the library; all keys and third-party API calls live on your backend.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Full setup (copy-paste)
|
|
66
|
+
|
|
67
|
+
1. Create or open an Angular app (Angular ^20.0.0).
|
|
68
|
+
|
|
69
|
+
2. Install the library and peer dependencies:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install profile-comparison-lib @angular/core@^20 @angular/common@^20
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
3. Add the backend URL provider in `app.config.ts`:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { ApplicationConfig } from '@angular/core';
|
|
79
|
+
import { provideHttpClient } from '@angular/common/http';
|
|
80
|
+
import { PROFILE_COMPARISON_API_BASE_URL } from 'profile-comparison-lib';
|
|
81
|
+
|
|
82
|
+
export const appConfig: ApplicationConfig = {
|
|
83
|
+
providers: [
|
|
84
|
+
provideHttpClient(),
|
|
85
|
+
{ provide: PROFILE_COMPARISON_API_BASE_URL, useValue: 'https://your-backend.com/api' },
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Or in `AppModule`:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { PROFILE_COMPARISON_API_BASE_URL } from 'profile-comparison-lib';
|
|
94
|
+
|
|
95
|
+
@NgModule({
|
|
96
|
+
providers: [
|
|
97
|
+
{ provide: PROFILE_COMPARISON_API_BASE_URL, useValue: 'https://your-backend.com/api' },
|
|
98
|
+
],
|
|
99
|
+
})
|
|
100
|
+
export class AppModule {}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
4. Import the module where you use the component:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { ProfileComparisonLibModule } from 'profile-comparison-lib';
|
|
107
|
+
|
|
108
|
+
@NgModule({
|
|
109
|
+
imports: [ProfileComparisonLibModule, ...],
|
|
110
|
+
})
|
|
111
|
+
export class YourModule {}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
5. Add the component to a template with required `[config]`:
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<lib-profile-comparison
|
|
118
|
+
[config]="config"
|
|
119
|
+
[fadeAllEdges]="fadeAllEdges"
|
|
120
|
+
(matrixDataChange)="onMatrixData($event)"
|
|
121
|
+
(rawLLMOutputChange)="onRawLLMOutput($event)"
|
|
122
|
+
(viewProfileClick)="onViewProfile($event)"
|
|
123
|
+
></lib-profile-comparison>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
6. Run the app and ensure your backend is reachable at the URL you provided.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Inputs and outputs
|
|
131
|
+
|
|
132
|
+
### Inputs
|
|
133
|
+
|
|
134
|
+
- **`config`** (required) — `IProfileConfig`:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"person1Interests": ["Gaming", "Programming", "AI/ML"],
|
|
139
|
+
"person2Interests": ["AI machine learning", "Mobile Games", "Data Science"],
|
|
140
|
+
"person3Interests": ["Board Games", "Machine Learning"],
|
|
141
|
+
"user1Image": "data:image/jpeg;base64,...",
|
|
142
|
+
"user2Image": "data:image/jpeg;base64,..."
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`user1Image` and `user2Image` can be data URLs or URLs; the library sends them to the backend as needed.
|
|
147
|
+
|
|
148
|
+
- **`fadeAllEdges`** (optional) — `boolean`, default `false`. When true, fades the outer edges of the profile area.
|
|
149
|
+
|
|
150
|
+
### Outputs
|
|
151
|
+
|
|
152
|
+
- **`matrixDataChange`** — `IMatrixData`: legend, headers, and rows for the similarity matrix.
|
|
153
|
+
|
|
154
|
+
Example payload:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"legend": [{ "abbr": "Gam", "full": "Gaming" }, { "abbr": "Pro", "full": "Programming" }],
|
|
159
|
+
"headers": ["Gam", "Pro", "AI/ML"],
|
|
160
|
+
"rows": [
|
|
161
|
+
{ "label": "Gam", "values": ["1.00", "0.2", "0.5"] },
|
|
162
|
+
{ "label": "Pro", "values": ["0.2", "1.00", "0.8"] }
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
- **`rawLLMOutputChange`** — `string`: raw LLM output from the backend (if any).
|
|
168
|
+
|
|
169
|
+
- **`viewProfileClick`** — `{ side: 'left' | 'right' }`: emitted when the user clicks “View Profile” on the left or right side. Use this to navigate or open a profile page.
|
|
170
|
+
|
|
171
|
+
Example:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
onViewProfile(payload: { side: 'left' | 'right' }) {
|
|
175
|
+
this.router.navigate(['/profile', payload.side]);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Backend API contract
|
|
182
|
+
|
|
183
|
+
The library calls your backend at `POST {baseUrl}/profile/compare-full` with body `{ config: IProfileConfig }`. The backend should return a display payload (face results, aligned lists, center item, matrix data, raw LLM output). See the **profile-comparison-server** repo and the lib interface `IComparisonPayload` for the response shape.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Building the library
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
ng build profile-comparison-lib
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Output is in `dist/profile-comparison-lib/` (FESM, typings, package.json). Peer dependencies: Angular `^20.0.0` only.
|