flen-crypto commited on
Commit
fdc0069
·
verified ·
1 Parent(s): 9bc6d93

ok so the user needs to have a little ai chat box when adding photos and data, as if the ai gives wrong info he needs to correct it and it needs to learn where it went wrong

Browse files
Files changed (2) hide show
  1. components/ai-chat.js +739 -0
  2. index.html +7 -4
components/ai-chat.js ADDED
@@ -0,0 +1,739 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class AIChat extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.messages = [];
5
+ this.corrections = {};
6
+ this.learningData = JSON.parse(localStorage.getItem('ai_learning_data') || '{}');
7
+ }
8
+
9
+ connectedCallback() {
10
+ this.attachShadow({ mode: 'open' });
11
+ this.render();
12
+ this.setupEventListeners();
13
+ }
14
+
15
+ render() {
16
+ this.shadowRoot.innerHTML = `
17
+ <style>
18
+ :host {
19
+ display: block;
20
+ }
21
+ .chat-container {
22
+ background: #1e293b;
23
+ border: 1px solid #334155;
24
+ border-radius: 12px;
25
+ overflow: hidden;
26
+ display: flex;
27
+ flex-direction: column;
28
+ height: 400px;
29
+ }
30
+ .chat-header {
31
+ background: linear-gradient(135deg, #7c3aed20 0%, #06b6d420 100%);
32
+ padding: 12px 16px;
33
+ border-bottom: 1px solid #334155;
34
+ display: flex;
35
+ align-items: center;
36
+ gap: 10px;
37
+ }
38
+ .chat-header i {
39
+ color: #7c3aed;
40
+ }
41
+ .chat-header h3 {
42
+ margin: 0;
43
+ font-size: 14px;
44
+ font-weight: 600;
45
+ color: #e2e8f0;
46
+ }
47
+ .status-badge {
48
+ margin-left: auto;
49
+ font-size: 11px;
50
+ padding: 4px 10px;
51
+ border-radius: 9999px;
52
+ background: #22c55e20;
53
+ color: #22c55e;
54
+ }
55
+ .status-badge.thinking {
56
+ background: #f59e0b20;
57
+ color: #f59e0b;
58
+ animation: pulse 1.5s infinite;
59
+ }
60
+ @keyframes pulse {
61
+ 0%, 100% { opacity: 1; }
62
+ 50% { opacity: 0.6; }
63
+ }
64
+ .messages-area {
65
+ flex: 1;
66
+ overflow-y: auto;
67
+ padding: 16px;
68
+ display: flex;
69
+ flex-direction: column;
70
+ gap: 12px;
71
+ }
72
+ .message {
73
+ max-width: 85%;
74
+ padding: 12px 14px;
75
+ border-radius: 12px;
76
+ font-size: 13px;
77
+ line-height: 1.5;
78
+ position: relative;
79
+ }
80
+ .message.ai {
81
+ align-self: flex-start;
82
+ background: #0f172a;
83
+ border: 1px solid #334155;
84
+ color: #e2e8f0;
85
+ }
86
+ .message.user {
87
+ align-self: flex-end;
88
+ background: #7c3aed;
89
+ color: white;
90
+ }
91
+ .message.correction {
92
+ border-color: #f59e0b;
93
+ background: #f59e0b10;
94
+ }
95
+ .message-meta {
96
+ font-size: 10px;
97
+ opacity: 0.6;
98
+ margin-top: 6px;
99
+ }
100
+ .correction-form {
101
+ margin-top: 10px;
102
+ padding: 10px;
103
+ background: #0f172a;
104
+ border-radius: 8px;
105
+ border: 1px dashed #475569;
106
+ }
107
+ .correction-form label {
108
+ display: block;
109
+ font-size: 11px;
110
+ color: #94a3b8;
111
+ margin-bottom: 6px;
112
+ }
113
+ .correction-input {
114
+ width: 100%;
115
+ padding: 8px 10px;
116
+ background: #1e293b;
117
+ border: 1px solid #475569;
118
+ border-radius: 6px;
119
+ color: #e2e8f0;
120
+ font-size: 12px;
121
+ margin-bottom: 8px;
122
+ }
123
+ .correction-input:focus {
124
+ outline: none;
125
+ border-color: #f59e0b;
126
+ }
127
+ .correction-actions {
128
+ display: flex;
129
+ gap: 8px;
130
+ }
131
+ .btn-small {
132
+ padding: 6px 12px;
133
+ border-radius: 6px;
134
+ font-size: 11px;
135
+ cursor: pointer;
136
+ border: none;
137
+ transition: all 0.2s;
138
+ }
139
+ .btn-confirm {
140
+ background: #22c55e;
141
+ color: white;
142
+ }
143
+ .btn-confirm:hover {
144
+ background: #16a34a;
145
+ }
146
+ .btn-correct {
147
+ background: #f59e0b;
148
+ color: #0f172a;
149
+ }
150
+ .btn-correct:hover {
151
+ background: #d97706;
152
+ }
153
+ .field-correction {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: 8px;
157
+ padding: 6px 10px;
158
+ background: #f59e0b15;
159
+ border-radius: 6px;
160
+ margin-top: 8px;
161
+ font-size: 11px;
162
+ }
163
+ .field-correction .field-name {
164
+ color: #f59e0b;
165
+ font-weight: 500;
166
+ }
167
+ .field-correction .arrow {
168
+ color: #64748b;
169
+ }
170
+ .field-correction .corrected {
171
+ color: #22c55e;
172
+ text-decoration: line-through;
173
+ opacity: 0.7;
174
+ }
175
+ .input-area {
176
+ padding: 12px 16px;
177
+ border-top: 1px solid #334155;
178
+ background: #0f172a;
179
+ }
180
+ .input-row {
181
+ display: flex;
182
+ gap: 10px;
183
+ }
184
+ .chat-input {
185
+ flex: 1;
186
+ padding: 10px 14px;
187
+ background: #1e293b;
188
+ border: 1px solid #475569;
189
+ border-radius: 8px;
190
+ color: #e2e8f0;
191
+ font-size: 13px;
192
+ }
193
+ .chat-input:focus {
194
+ outline: none;
195
+ border-color: #7c3aed;
196
+ }
197
+ .send-btn {
198
+ padding: 10px 16px;
199
+ background: #7c3aed;
200
+ border: none;
201
+ border-radius: 8px;
202
+ color: white;
203
+ cursor: pointer;
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: center;
207
+ transition: all 0.2s;
208
+ }
209
+ .send-btn:hover {
210
+ background: #6d28d9;
211
+ }
212
+ .send-btn:disabled {
213
+ opacity: 0.5;
214
+ cursor: not-allowed;
215
+ }
216
+ .quick-actions {
217
+ display: flex;
218
+ gap: 8px;
219
+ margin-top: 10px;
220
+ flex-wrap: wrap;
221
+ }
222
+ .quick-btn {
223
+ padding: 6px 12px;
224
+ background: #1e293b;
225
+ border: 1px solid #475569;
226
+ border-radius: 16px;
227
+ color: #94a3b8;
228
+ font-size: 11px;
229
+ cursor: pointer;
230
+ transition: all 0.2s;
231
+ }
232
+ .quick-btn:hover {
233
+ border-color: #7c3aed;
234
+ color: #7c3aed;
235
+ }
236
+ .detected-fields {
237
+ margin-top: 12px;
238
+ padding: 12px;
239
+ background: #0f172a;
240
+ border-radius: 8px;
241
+ border: 1px solid #334155;
242
+ }
243
+ .detected-fields h4 {
244
+ margin: 0 0 10px 0;
245
+ font-size: 12px;
246
+ color: #94a3b8;
247
+ font-weight: 500;
248
+ }
249
+ .field-grid {
250
+ display: grid;
251
+ grid-template-columns: repeat(2, 1fr);
252
+ gap: 8px;
253
+ }
254
+ .field-item {
255
+ display: flex;
256
+ flex-direction: column;
257
+ gap: 2px;
258
+ }
259
+ .field-label {
260
+ font-size: 10px;
261
+ color: #64748b;
262
+ text-transform: uppercase;
263
+ }
264
+ .field-value {
265
+ font-size: 12px;
266
+ color: #e2e8f0;
267
+ font-weight: 500;
268
+ }
269
+ .field-value.corrected {
270
+ color: #22c55e;
271
+ }
272
+ .field-value.wrong {
273
+ color: #ef4444;
274
+ text-decoration: line-through;
275
+ }
276
+ .confidence-indicator {
277
+ display: inline-flex;
278
+ align-items: center;
279
+ gap: 4px;
280
+ font-size: 10px;
281
+ padding: 2px 8px;
282
+ border-radius: 10px;
283
+ margin-left: 6px;
284
+ }
285
+ .confidence-high {
286
+ background: #22c55e20;
287
+ color: #22c55e;
288
+ }
289
+ .confidence-medium {
290
+ background: #f59e0b20;
291
+ color: #f59e0b;
292
+ }
293
+ .confidence-low {
294
+ background: #ef444420;
295
+ color: #ef4444;
296
+ }
297
+ .learned-badge {
298
+ display: inline-flex;
299
+ align-items: center;
300
+ gap: 4px;
301
+ font-size: 10px;
302
+ padding: 4px 10px;
303
+ border-radius: 10px;
304
+ background: #06b6d420;
305
+ color: #06b6d4;
306
+ margin-top: 8px;
307
+ }
308
+ .typing-indicator {
309
+ display: flex;
310
+ gap: 4px;
311
+ padding: 12px 14px;
312
+ }
313
+ .typing-indicator span {
314
+ width: 8px;
315
+ height: 8px;
316
+ background: #7c3aed;
317
+ border-radius: 50%;
318
+ animation: bounce 1.4s infinite ease-in-out both;
319
+ }
320
+ .typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
321
+ .typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
322
+ @keyframes bounce {
323
+ 0%, 80%, 100% { transform: scale(0); }
324
+ 40% { transform: scale(1); }
325
+ }
326
+ </style>
327
+
328
+ <div class="chat-container">
329
+ <div class="chat-header">
330
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
331
+ <h3>AI Assistant</h3>
332
+ <span class="status-badge" id="statusBadge">Ready</span>
333
+ </div>
334
+
335
+ <div class="messages-area" id="messagesArea">
336
+ <div class="message ai">
337
+ Hi! I'll analyze your record photos and help identify the details. If I get anything wrong, just correct me—I'll learn from it for next time!
338
+ </div>
339
+ </div>
340
+
341
+ <div class="input-area">
342
+ <div class="input-row">
343
+ <input type="text" class="chat-input" id="chatInput" placeholder="Ask a question or correct a field..." />
344
+ <button class="send-btn" id="sendBtn">
345
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
346
+ </button>
347
+ </div>
348
+ <div class="quick-actions" id="quickActions">
349
+ <button class="quick-btn" data-action="confirm-all">✓ All Correct</button>
350
+ <button class="quick-btn" data-action="wrong-artist">Wrong Artist</button>
351
+ <button class="quick-btn" data-action="wrong-title">Wrong Title</button>
352
+ <button class="quick-btn" data-action="wrong-year">Wrong Year</button>
353
+ <button class="quick-btn" data-action="wrong-cat">Wrong Catalogue</button>
354
+ </div>
355
+ </div>
356
+ </div>
357
+ `;
358
+ }
359
+
360
+ setupEventListeners() {
361
+ const input = this.shadowRoot.getElementById('chatInput');
362
+ const sendBtn = this.shadowRoot.getElementById('sendBtn');
363
+ const quickActions = this.shadowRoot.getElementById('quickActions');
364
+
365
+ sendBtn.addEventListener('click', () => this.handleSend());
366
+ input.addEventListener('keypress', (e) => {
367
+ if (e.key === 'Enter') this.handleSend();
368
+ });
369
+
370
+ quickActions.addEventListener('click', (e) => {
371
+ if (e.target.classList.contains('quick-btn')) {
372
+ this.handleQuickAction(e.target.dataset.action);
373
+ }
374
+ });
375
+ }
376
+
377
+ handleSend() {
378
+ const input = this.shadowRoot.getElementById('chatInput');
379
+ const message = input.value.trim();
380
+ if (!message) return;
381
+
382
+ this.addMessage(message, 'user');
383
+ input.value = '';
384
+
385
+ // Process the message
386
+ this.processUserMessage(message);
387
+ }
388
+
389
+ handleQuickAction(action) {
390
+ switch(action) {
391
+ case 'confirm-all':
392
+ this.confirmAllFields();
393
+ break;
394
+ case 'wrong-artist':
395
+ this.requestCorrection('artist');
396
+ break;
397
+ case 'wrong-title':
398
+ this.requestCorrection('title');
399
+ break;
400
+ case 'wrong-year':
401
+ this.requestCorrection('year');
402
+ break;
403
+ case 'wrong-cat':
404
+ this.requestCorrection('catalogueNumber');
405
+ break;
406
+ }
407
+ }
408
+
409
+ addMessage(text, sender, options = {}) {
410
+ const messagesArea = this.shadowRoot.getElementById('messagesArea');
411
+ const messageDiv = document.createElement('div');
412
+ messageDiv.className = `message ${sender} ${options.isCorrection ? 'correction' : ''}`;
413
+
414
+ let content = text;
415
+ if (options.field && options.originalValue !== undefined) {
416
+ content += `
417
+ <div class="field-correction">
418
+ <span class="field-name">${options.field}:</span>
419
+ <span class="corrected">${options.originalValue}</span>
420
+ <span class="arrow">→</span>
421
+ <span>${options.newValue}</span>
422
+ </div>
423
+ `;
424
+ }
425
+
426
+ messageDiv.innerHTML = content;
427
+ if (options.meta) {
428
+ messageDiv.innerHTML += `<div class="message-meta">${options.meta}</div>`;
429
+ }
430
+
431
+ messagesArea.appendChild(messageDiv);
432
+ messagesArea.scrollTop = messagesArea.scrollHeight;
433
+
434
+ this.messages.push({ sender, text, ...options });
435
+ }
436
+
437
+ showTyping() {
438
+ const messagesArea = this.shadowRoot.getElementById('messagesArea');
439
+ const typingDiv = document.createElement('div');
440
+ typingDiv.className = 'message ai typing-indicator';
441
+ typingDiv.id = 'typingIndicator';
442
+ typingDiv.innerHTML = '<span></span><span></span><span></span>';
443
+ messagesArea.appendChild(typingDiv);
444
+ messagesArea.scrollTop = messagesArea.scrollHeight;
445
+
446
+ this.setStatus('thinking');
447
+ }
448
+
449
+ hideTyping() {
450
+ const typing = this.shadowRoot.getElementById('typingIndicator');
451
+ if (typing) typing.remove();
452
+ this.setStatus('ready');
453
+ }
454
+
455
+ setStatus(status) {
456
+ const badge = this.shadowRoot.getElementById('statusBadge');
457
+ badge.className = `status-badge ${status === 'thinking' ? 'thinking' : ''}`;
458
+ badge.textContent = status === 'thinking' ? 'Analyzing...' : 'Ready';
459
+ }
460
+
461
+ // Called when OCR results come in
462
+ showDetectionResults(data) {
463
+ this.currentDetection = data;
464
+ this.corrections = {};
465
+
466
+ const messagesArea = this.shadowRoot.getElementById('messagesArea');
467
+
468
+ // Build detected fields display
469
+ const fieldsHtml = `
470
+ <div class="detected-fields">
471
+ <h4>Detected Information
472
+ <span class="confidence-indicator confidence-${data.confidence || 'medium'}">
473
+ ${data.confidence || 'medium'} confidence
474
+ </span>
475
+ </h4>
476
+ <div class="field-grid">
477
+ ${this.renderField('Artist', data.artist)}
478
+ ${this.renderField('Title', data.title)}
479
+ ${this.renderField('Year', data.year)}
480
+ ${this.renderField('Catalogue #', data.catalogueNumber)}
481
+ ${this.renderField('Label', data.label)}
482
+ ${this.renderField('Country', data.country)}
483
+ ${this.renderField('Format', data.format)}
484
+ ${this.renderField('Genre', data.genre)}
485
+ </div>
486
+ ${this.hasLearnedCorrections() ? `
487
+ <div class="learned-badge">
488
+ <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
489
+ Applied ${Object.keys(this.getRelevantLearnings(data)).length} learned corrections
490
+ </div>
491
+ ` : ''}
492
+ </div>
493
+ `;
494
+
495
+ const messageDiv = document.createElement('div');
496
+ messageDiv.className = 'message ai';
497
+ messageDiv.innerHTML = `
498
+ I've analyzed your photos! Here's what I found:
499
+ ${fieldsHtml}
500
+ <p style="margin-top: 12px; font-size: 12px; color: #94a3b8;">
501
+ Please review these details. If anything looks wrong, click "Wrong [Field]" or type a correction like "The artist is actually..."
502
+ </p>
503
+ `;
504
+
505
+ messagesArea.appendChild(messageDiv);
506
+ messagesArea.scrollTop = messagesArea.scrollHeight;
507
+
508
+ // Apply any learned corrections automatically
509
+ this.applyLearnedCorrections(data);
510
+ }
511
+
512
+ renderField(label, value) {
513
+ const corrected = this.corrections[label.toLowerCase()];
514
+ const displayValue = corrected || value || '-';
515
+ const isCorrected = !!corrected;
516
+
517
+ return `
518
+ <div class="field-item">
519
+ <span class="field-label">${label}</span>
520
+ <span class="field-value ${isCorrected ? 'corrected' : ''} ${value === null ? 'wrong' : ''}">
521
+ ${displayValue}
522
+ </span>
523
+ </div>
524
+ `;
525
+ }
526
+
527
+ requestCorrection(field) {
528
+ const currentValue = this.corrections[field] || this.currentDetection?.[field] || '';
529
+
530
+ this.addMessage(
531
+ `What's the correct ${field}?`,
532
+ 'ai',
533
+ { meta: 'Type the correct value below' }
534
+ );
535
+
536
+ // Store that we're waiting for this correction
537
+ this.pendingCorrection = field;
538
+
539
+ // Focus input
540
+ this.shadowRoot.getElementById('chatInput').focus();
541
+ }
542
+
543
+ processUserMessage(message) {
544
+ // Check if this is a correction response
545
+ if (this.pendingCorrection) {
546
+ this.applyCorrection(this.pendingCorrection, message);
547
+ this.pendingCorrection = null;
548
+ return;
549
+ }
550
+
551
+ // Check for natural language corrections
552
+ const correctionPatterns = [
553
+ { regex: /(?:the\s+)?artist\s+(?:is|should be|was)\s+(.+)/i, field: 'artist' },
554
+ { regex: /(?:the\s+)?title\s+(?:is|should be|was)\s+(.+)/i, field: 'title' },
555
+ { regex: /(?:the\s+)?year\s+(?:is|should be|was)\s+(\d{4})/i, field: 'year' },
556
+ { regex: /(?:the\s+)?catalog(?:ue)?\s*(?:#|number)?\s+(?:is|should be|was)\s+(.+)/i, field: 'catalogueNumber' },
557
+ { regex: /(?:the\s+)?label\s+(?:is|should be|was)\s+(.+)/i, field: 'label' },
558
+ { regex: /wrong\s+artist/i, field: 'artist', needsValue: true },
559
+ { regex: /wrong\s+title/i, field: 'title', needsValue: true },
560
+ { regex: /wrong\s+year/i, field: 'year', needsValue: true },
561
+ ];
562
+
563
+ for (const pattern of correctionPatterns) {
564
+ const match = message.match(pattern.regex);
565
+ if (match) {
566
+ if (pattern.needsValue) {
567
+ this.requestCorrection(pattern.field);
568
+ } else {
569
+ this.applyCorrection(pattern.field, match[1].trim());
570
+ }
571
+ return;
572
+ }
573
+ }
574
+
575
+ // General question or confirmation
576
+ this.showTyping();
577
+ setTimeout(() => {
578
+ this.hideTyping();
579
+ this.addMessage(
580
+ "I understand. You can tell me about any other corrections needed, or click 'All Correct' if everything looks good!",
581
+ 'ai'
582
+ );
583
+ }, 800);
584
+ }
585
+
586
+ applyCorrection(field, newValue) {
587
+ const originalValue = this.currentDetection?.[field];
588
+ this.corrections[field] = newValue;
589
+
590
+ // Store learning data
591
+ this.learnFromCorrection(field, originalValue, newValue);
592
+
593
+ this.addMessage(
594
+ `Thanks! I've corrected the ${field}.`,
595
+ 'ai',
596
+ {
597
+ isCorrection: true,
598
+ field: field.charAt(0).toUpperCase() + field.slice(1),
599
+ originalValue,
600
+ newValue,
601
+ meta: 'Learning saved for future analyses'
602
+ }
603
+ );
604
+
605
+ // Update the displayed fields
606
+ this.updateDetectedFieldsDisplay();
607
+
608
+ // Dispatch event for parent to update form
609
+ this.dispatchEvent(new CustomEvent('field-corrected', {
610
+ detail: { field, value: newValue, originalValue },
611
+ bubbles: true,
612
+ composed: true
613
+ }));
614
+ }
615
+
616
+ learnFromCorrection(field, originalValue, correctedValue) {
617
+ if (!originalValue || !correctedValue) return;
618
+
619
+ // Create a learning key based on context
620
+ const context = this.buildContextKey();
621
+ const key = `${context}:${field}`;
622
+
623
+ if (!this.learningData[key]) {
624
+ this.learningData[key] = [];
625
+ }
626
+
627
+ this.learningData[key].push({
628
+ from: originalValue,
629
+ to: correctedValue,
630
+ timestamp: Date.now(),
631
+ detection: this.currentDetection
632
+ });
633
+
634
+ // Keep only last 50 corrections per key
635
+ if (this.learningData[key].length > 50) {
636
+ this.learningData[key] = this.learningData[key].slice(-50);
637
+ }
638
+
639
+ localStorage.setItem('ai_learning_data', JSON.stringify(this.learningData));
640
+ }
641
+
642
+ buildContextKey() {
643
+ // Create a fuzzy context key from available data
644
+ const d = this.currentDetection || {};
645
+ const artist = (d.artist || '').toLowerCase().replace(/[^\w]/g, '').slice(0, 10);
646
+ const title = (d.title || '').toLowerCase().replace(/[^\w]/g, '').slice(0, 10);
647
+ return `${artist}_${title}`;
648
+ }
649
+
650
+ getRelevantLearnings(currentDetection) {
651
+ const context = this.buildContextKey();
652
+ const learnings = {};
653
+
654
+ for (const [key, corrections] of Object.entries(this.learningData)) {
655
+ if (key.startsWith(context)) {
656
+ const field = key.split(':')[1];
657
+ // Get most common correction
658
+ const counts = {};
659
+ corrections.forEach(c => {
660
+ const k = `${c.from}->${c.to}`;
661
+ counts[k] = (counts[k] || 0) + 1;
662
+ });
663
+ const mostCommon = Object.entries(counts).sort((a, b) => b[1] - a[1])[0];
664
+ if (mostCommon && mostCommon[1] >= 2) { // Need at least 2 occurrences
665
+ const [from, to] = mostCommon[0].split('->');
666
+ learnings[field] = { from, to, confidence: mostCommon[1] };
667
+ }
668
+ }
669
+ }
670
+
671
+ return learnings;
672
+ }
673
+
674
+ hasLearnedCorrections() {
675
+ return Object.keys(this.getRelevantLearnings(this.currentDetection)).length > 0;
676
+ }
677
+
678
+ applyLearnedCorrections(data) {
679
+ const learnings = this.getRelevantLearnings(data);
680
+
681
+ for (const [field, learning] of Object.entries(learnings)) {
682
+ if (data[field] === learning.from) {
683
+ this.corrections[field] = learning.to;
684
+ console.log(`Applied learned correction: ${field} "${learning.from}" -> "${learning.to}"`);
685
+ }
686
+ }
687
+ }
688
+
689
+ updateDetectedFieldsDisplay() {
690
+ // Remove old detection display and show updated one
691
+ const messages = this.shadowRoot.querySelectorAll('.message.ai');
692
+ messages.forEach(m => {
693
+ if (m.querySelector('.detected-fields')) {
694
+ m.remove();
695
+ }
696
+ });
697
+
698
+ // Re-show with corrections applied
699
+ const correctedData = { ...this.currentDetection, ...this.corrections };
700
+ this.showDetectionResults(correctedData);
701
+ }
702
+
703
+ confirmAllFields() {
704
+ this.addMessage(
705
+ "Great! All information confirmed. I'll use these details for your listing.",
706
+ 'ai',
707
+ { meta: 'Proceeding with verified information' }
708
+ );
709
+
710
+ this.dispatchEvent(new CustomEvent('all-confirmed', {
711
+ detail: {
712
+ data: { ...this.currentDetection, ...this.corrections },
713
+ corrections: this.corrections
714
+ },
715
+ bubbles: true,
716
+ composed: true
717
+ }));
718
+ }
719
+
720
+ getCorrectedData() {
721
+ return { ...this.currentDetection, ...this.corrections };
722
+ }
723
+
724
+ clear() {
725
+ this.messages = [];
726
+ this.corrections = {};
727
+ this.currentDetection = null;
728
+ this.pendingCorrection = null;
729
+
730
+ const messagesArea = this.shadowRoot.getElementById('messagesArea');
731
+ messagesArea.innerHTML = `
732
+ <div class="message ai">
733
+ Hi! I'll analyze your record photos and help identify the details. If I get anything wrong, just correct me—I'll learn from it for next time!
734
+ </div>
735
+ `;
736
+ }
737
+ }
738
+
739
+ customElements.define('ai-chat', AIChat);
index.html CHANGED
@@ -46,8 +46,7 @@
46
  </div>
47
  <!-- Main Content -->
48
  <main class="pt-20 pb-12 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
49
-
50
- <!-- Hero Section -->
51
  <section class="mb-12 text-center">
52
  <div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-primary/10 border border-primary/30 mb-6">
53
  <i data-feather="disc" class="w-4 h-4 text-primary"></i>
@@ -73,15 +72,18 @@
73
  <section class="mb-12">
74
  <div class="bg-surface-light rounded-2xl p-8 border border-gray-800">
75
  <div class="flex flex-col md:flex-row gap-8">
76
-
77
  <!-- Upload Zone -->
78
  <div class="flex-1">
79
  <h2 class="text-xl font-semibold mb-4 flex items-center gap-2">
80
  <i data-feather="upload-cloud" class="w-5 h-5 text-secondary"></i>
81
  Upload Record Photos
82
  </h2>
 
 
 
 
83
  <div id="dropZone" class="border-2 border-dashed border-gray-600 rounded-xl p-12 text-center transition-all hover:border-primary hover:bg-primary/5 cursor-pointer group relative">
84
- <div id="uploadSpinner" class="absolute inset-0 bg-surface-light/90 rounded-xl flex flex-col items-center justify-center hidden z-10">
85
  <div class="w-12 h-12 border-4 border-primary/30 border-t-primary rounded-full animate-spin mb-3"></div>
86
  <p class="text-sm text-gray-400 mb-4">Analyzing images with AI...</p>
87
  <!-- Analysis Progress Bar -->
@@ -350,6 +352,7 @@
350
  <script src="components/enhanced-ocr-service.js"></script>
351
  <script src="components/discogs-service.js"></script>
352
  <script src="components/deepseek-service.js"></script>
 
353
  <!-- Main Script -->
354
  <script src="script.js"></script>
355
  <script>feather.replace();</script>
 
46
  </div>
47
  <!-- Main Content -->
48
  <main class="pt-20 pb-12 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
49
+ <!-- Hero Section -->
 
50
  <section class="mb-12 text-center">
51
  <div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-primary/10 border border-primary/30 mb-6">
52
  <i data-feather="disc" class="w-4 h-4 text-primary"></i>
 
72
  <section class="mb-12">
73
  <div class="bg-surface-light rounded-2xl p-8 border border-gray-800">
74
  <div class="flex flex-col md:flex-row gap-8">
 
75
  <!-- Upload Zone -->
76
  <div class="flex-1">
77
  <h2 class="text-xl font-semibold mb-4 flex items-center gap-2">
78
  <i data-feather="upload-cloud" class="w-5 h-5 text-secondary"></i>
79
  Upload Record Photos
80
  </h2>
81
+
82
+ <!-- AI Chat Interface -->
83
+ <ai-chat id="aiChatBox" class="mb-4"></ai-chat>
84
+
85
  <div id="dropZone" class="border-2 border-dashed border-gray-600 rounded-xl p-12 text-center transition-all hover:border-primary hover:bg-primary/5 cursor-pointer group relative">
86
+ <div id="uploadSpinner" class="absolute inset-0 bg-surface-light/90 rounded-xl flex flex-col items-center justify-center hidden z-10">
87
  <div class="w-12 h-12 border-4 border-primary/30 border-t-primary rounded-full animate-spin mb-3"></div>
88
  <p class="text-sm text-gray-400 mb-4">Analyzing images with AI...</p>
89
  <!-- Analysis Progress Bar -->
 
352
  <script src="components/enhanced-ocr-service.js"></script>
353
  <script src="components/discogs-service.js"></script>
354
  <script src="components/deepseek-service.js"></script>
355
+ <script src="components/ai-chat.js"></script>
356
  <!-- Main Script -->
357
  <script src="script.js"></script>
358
  <script>feather.replace();</script>