Enterprise AI co-pilot with ghost-to-cockpit interface, privacy blur, and titanium governance for CRM platforms
Size
35.9 KB
Version
5.7.1
Created
Jan 23, 2026
Updated
11 days ago
1// ==UserScript==
2// @name Solvaria Sidekick - AI CRM Co-Pilot
3// @description Enterprise AI co-pilot with ghost-to-cockpit interface, privacy blur, and titanium governance for CRM platforms
4// @version 5.7.1
5// @match https://*.salesforce.com/*
6// @match https://*.lightning.force.com/*
7// @match https://*.zendesk.com/*
8// @match https://*.robomonkey.io/*
9// @icon https://robomonkey.io/favicon.ico
10// @grant GM.getValue
11// @grant GM.setValue
12// @grant GM.xmlhttpRequest
13// ==/UserScript==
14(function() {
15 'use strict';
16
17 // ============================================================================
18 // SOLVARIA SIDEKICK - ENTERPRISE AI CRM CO-PILOT
19 // Version 5.7.0 - Ghost-to-Cockpit Architecture
20 // ============================================================================
21
22 console.log('🚀 Solvaria Sidekick v5.7 - Initializing...');
23
24 // ============================================================================
25 // CONFIGURATION & CONSTANTS
26 // ============================================================================
27
28 const CONFIG = {
29 BACKEND_URL: 'https://api.solvaria.io/v1/analyze', // Replace with actual backend
30 SSE_STREAM_URL: 'https://api.solvaria.io/v1/stream',
31 PRIORITY_THRESHOLD: 50,
32 SIDEBAR_WIDTH: 380,
33 ANIMATION_DURATION: 400,
34 GLOW_WIDTH: 2,
35 Z_INDEX_MAX: 2147483647,
36 COLORS: {
37 background: '#0A0A0B',
38 accent: '#4F46E5',
39 border: 'rgba(255, 255, 255, 0.1)',
40 text: '#FFFFFF',
41 textMuted: '#9CA3AF',
42 success: '#10B981',
43 warning: '#F59E0B',
44 danger: '#EF4444'
45 }
46 };
47
48 // ============================================================================
49 // STATE MANAGEMENT
50 // ============================================================================
51
52 const STATE = {
53 isExpanded: false,
54 priorityScore: 0,
55 currentAnalysis: null,
56 blockedButtons: new Set(),
57 shadowRoot: null,
58 glowElement: null,
59 sidebarElement: null
60 };
61
62 // ============================================================================
63 // UTILITY FUNCTIONS
64 // ============================================================================
65
66 function debounce(func, wait) {
67 let timeout;
68 return function executedFunction(...args) {
69 const later = () => {
70 clearTimeout(timeout);
71 func(...args);
72 };
73 clearTimeout(timeout);
74 timeout = setTimeout(later, wait);
75 };
76 }
77
78 function detectCRMPlatform() {
79 const hostname = window.location.hostname;
80 if (hostname.includes('salesforce') || hostname.includes('force.com')) {
81 return 'salesforce';
82 } else if (hostname.includes('zendesk')) {
83 return 'zendesk';
84 }
85 return 'unknown';
86 }
87
88 function getCRMSubmitButtons() {
89 const platform = detectCRMPlatform();
90 const selectors = {
91 salesforce: [
92 'button.slds-button_brand',
93 'button[type="submit"].slds-button',
94 'button[title*="Save"]',
95 'button[title*="Submit"]'
96 ],
97 zendesk: [
98 'button.c-btn--primary',
99 'button[type="submit"]',
100 'button[data-test-id="submit"]'
101 ],
102 unknown: ['button[type="submit"]', 'button.btn-primary']
103 };
104
105 const buttons = [];
106 (selectors[platform] || selectors.unknown).forEach(selector => {
107 buttons.push(...document.querySelectorAll(selector));
108 });
109 return buttons;
110 }
111
112 // ============================================================================
113 // SHADOW DOM ARCHITECTURE
114 // ============================================================================
115
116 function createShadowDOMContainer() {
117 console.log('🔒 Creating Shadow DOM container with maximum isolation...');
118
119 // Create host element
120 const host = document.createElement('div');
121 host.id = 'solvaria-sidekick-host';
122 host.style.cssText = `
123 position: fixed;
124 top: 0;
125 right: 0;
126 height: 100vh;
127 width: 0;
128 z-index: ${CONFIG.Z_INDEX_MAX};
129 pointer-events: none;
130 `;
131
132 // Attach shadow root with closed mode for maximum security
133 const shadowRoot = host.attachShadow({ mode: 'closed' });
134 STATE.shadowRoot = shadowRoot;
135
136 // Inject styles into shadow DOM
137 const styleSheet = document.createElement('style');
138 styleSheet.textContent = getShadowDOMStyles();
139 shadowRoot.appendChild(styleSheet);
140
141 // Append to body
142 document.body.appendChild(host);
143
144 return { host, shadowRoot };
145 }
146
147 function getShadowDOMStyles() {
148 return `
149 * {
150 box-sizing: border-box;
151 margin: 0;
152 padding: 0;
153 }
154
155 :host {
156 all: initial;
157 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
158 }
159
160 /* GLOW TRIGGER */
161 .solvaria-glow {
162 position: fixed;
163 top: 0;
164 right: 0;
165 width: ${CONFIG.GLOW_WIDTH}px;
166 height: 100vh;
167 background: linear-gradient(180deg, ${CONFIG.COLORS.accent}, #7C3AED);
168 cursor: pointer;
169 pointer-events: auto;
170 animation: pulse 2s ease-in-out infinite;
171 transition: width 0.3s ease;
172 }
173
174 .solvaria-glow:hover {
175 width: 4px;
176 }
177
178 @keyframes pulse {
179 0%, 100% { opacity: 0.6; box-shadow: 0 0 10px ${CONFIG.COLORS.accent}; }
180 50% { opacity: 1; box-shadow: 0 0 20px ${CONFIG.COLORS.accent}, 0 0 40px ${CONFIG.COLORS.accent}; }
181 }
182
183 /* SIDEBAR COCKPIT */
184 .solvaria-sidebar {
185 position: fixed;
186 top: 0;
187 right: 0;
188 width: ${CONFIG.SIDEBAR_WIDTH}px;
189 height: 100vh;
190 background: ${CONFIG.COLORS.background};
191 border-left: 1px solid ${CONFIG.COLORS.border};
192 backdrop-filter: blur(20px);
193 display: flex;
194 flex-direction: column;
195 pointer-events: auto;
196 transform: translateX(100%);
197 transition: transform ${CONFIG.ANIMATION_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1);
198 overflow: hidden;
199 }
200
201 .solvaria-sidebar.expanded {
202 transform: translateX(0);
203 }
204
205 /* HEADER */
206 .solvaria-header {
207 padding: 20px;
208 border-bottom: 1px solid ${CONFIG.COLORS.border};
209 display: flex;
210 align-items: center;
211 justify-content: space-between;
212 }
213
214 .solvaria-logo {
215 display: flex;
216 align-items: center;
217 gap: 10px;
218 }
219
220 .solvaria-logo-icon {
221 width: 32px;
222 height: 32px;
223 background: linear-gradient(135deg, ${CONFIG.COLORS.accent}, #7C3AED);
224 border-radius: 8px;
225 display: flex;
226 align-items: center;
227 justify-content: center;
228 font-size: 18px;
229 }
230
231 .solvaria-logo-text {
232 font-size: 16px;
233 font-weight: 600;
234 color: ${CONFIG.COLORS.text};
235 }
236
237 .solvaria-close-btn {
238 background: transparent;
239 border: none;
240 color: ${CONFIG.COLORS.textMuted};
241 cursor: pointer;
242 font-size: 20px;
243 padding: 5px;
244 transition: color 0.2s;
245 }
246
247 .solvaria-close-btn:hover {
248 color: ${CONFIG.COLORS.text};
249 }
250
251 /* CONTENT AREA */
252 .solvaria-content {
253 flex: 1;
254 overflow-y: auto;
255 padding: 20px;
256 }
257
258 .solvaria-content::-webkit-scrollbar {
259 width: 6px;
260 }
261
262 .solvaria-content::-webkit-scrollbar-track {
263 background: transparent;
264 }
265
266 .solvaria-content::-webkit-scrollbar-thumb {
267 background: ${CONFIG.COLORS.border};
268 border-radius: 3px;
269 }
270
271 /* ANALYSIS CARD */
272 .analysis-card {
273 background: rgba(255, 255, 255, 0.03);
274 border: 1px solid ${CONFIG.COLORS.border};
275 border-radius: 12px;
276 padding: 16px;
277 margin-bottom: 16px;
278 transition: all 0.3s ease;
279 }
280
281 .analysis-card:hover {
282 background: rgba(255, 255, 255, 0.05);
283 border-color: ${CONFIG.COLORS.accent};
284 }
285
286 .card-header {
287 display: flex;
288 align-items: center;
289 justify-content: space-between;
290 margin-bottom: 12px;
291 }
292
293 .card-title {
294 font-size: 14px;
295 font-weight: 600;
296 color: ${CONFIG.COLORS.text};
297 }
298
299 .priority-badge {
300 padding: 4px 8px;
301 border-radius: 6px;
302 font-size: 11px;
303 font-weight: 600;
304 text-transform: uppercase;
305 }
306
307 .priority-high {
308 background: rgba(239, 68, 68, 0.2);
309 color: ${CONFIG.COLORS.danger};
310 }
311
312 .priority-medium {
313 background: rgba(245, 158, 11, 0.2);
314 color: ${CONFIG.COLORS.warning};
315 }
316
317 .priority-low {
318 background: rgba(16, 185, 129, 0.2);
319 color: ${CONFIG.COLORS.success};
320 }
321
322 .card-content {
323 font-size: 13px;
324 color: ${CONFIG.COLORS.textMuted};
325 line-height: 1.6;
326 }
327
328 /* PRIVACY BLUR */
329 .sensitive-data {
330 filter: blur(5px);
331 transition: filter 0.3s ease;
332 cursor: pointer;
333 display: inline-block;
334 padding: 2px 6px;
335 background: rgba(79, 70, 229, 0.1);
336 border-radius: 4px;
337 }
338
339 .sensitive-data:hover {
340 filter: blur(0);
341 }
342
343 /* TITANIUM GOVERNANCE ALERT */
344 .governance-alert {
345 background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(220, 38, 38, 0.1));
346 border: 2px solid ${CONFIG.COLORS.danger};
347 border-radius: 12px;
348 padding: 16px;
349 margin-bottom: 16px;
350 animation: alertPulse 2s ease-in-out infinite;
351 }
352
353 @keyframes alertPulse {
354 0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
355 50% { box-shadow: 0 0 0 8px rgba(239, 68, 68, 0); }
356 }
357
358 .governance-alert-title {
359 display: flex;
360 align-items: center;
361 gap: 8px;
362 font-size: 14px;
363 font-weight: 700;
364 color: ${CONFIG.COLORS.danger};
365 margin-bottom: 8px;
366 }
367
368 .governance-alert-message {
369 font-size: 13px;
370 color: ${CONFIG.COLORS.text};
371 line-height: 1.5;
372 }
373
374 /* ACTION BUTTONS */
375 .action-button {
376 width: 100%;
377 padding: 12px;
378 background: ${CONFIG.COLORS.accent};
379 color: ${CONFIG.COLORS.text};
380 border: none;
381 border-radius: 8px;
382 font-size: 14px;
383 font-weight: 600;
384 cursor: pointer;
385 transition: all 0.2s;
386 margin-top: 12px;
387 }
388
389 .action-button:hover {
390 background: #4338CA;
391 transform: translateY(-1px);
392 box-shadow: 0 4px 12px rgba(79, 70, 229, 0.4);
393 }
394
395 .action-button:active {
396 transform: translateY(0);
397 }
398
399 .action-button.secondary {
400 background: rgba(255, 255, 255, 0.05);
401 border: 1px solid ${CONFIG.COLORS.border};
402 }
403
404 .action-button.secondary:hover {
405 background: rgba(255, 255, 255, 0.1);
406 }
407
408 /* STREAMING RESPONSE */
409 .streaming-response {
410 background: rgba(79, 70, 229, 0.05);
411 border: 1px solid ${CONFIG.COLORS.accent};
412 border-radius: 8px;
413 padding: 12px;
414 font-size: 13px;
415 color: ${CONFIG.COLORS.text};
416 line-height: 1.6;
417 font-family: 'SF Mono', Monaco, monospace;
418 white-space: pre-wrap;
419 word-wrap: break-word;
420 }
421
422 .typing-cursor {
423 display: inline-block;
424 width: 2px;
425 height: 14px;
426 background: ${CONFIG.COLORS.accent};
427 margin-left: 2px;
428 animation: blink 1s step-end infinite;
429 }
430
431 @keyframes blink {
432 50% { opacity: 0; }
433 }
434
435 /* FOOTER ROI */
436 .solvaria-footer {
437 padding: 16px 20px;
438 border-top: 1px solid ${CONFIG.COLORS.border};
439 background: rgba(79, 70, 229, 0.05);
440 }
441
442 .roi-display {
443 display: flex;
444 align-items: center;
445 justify-content: space-between;
446 }
447
448 .roi-label {
449 font-size: 12px;
450 color: ${CONFIG.COLORS.textMuted};
451 text-transform: uppercase;
452 letter-spacing: 0.5px;
453 }
454
455 .roi-value {
456 font-size: 20px;
457 font-weight: 700;
458 color: ${CONFIG.COLORS.success};
459 }
460
461 /* LOADING STATE */
462 .loading-spinner {
463 display: inline-block;
464 width: 16px;
465 height: 16px;
466 border: 2px solid ${CONFIG.COLORS.border};
467 border-top-color: ${CONFIG.COLORS.accent};
468 border-radius: 50%;
469 animation: spin 0.8s linear infinite;
470 }
471
472 @keyframes spin {
473 to { transform: rotate(360deg); }
474 }
475 `;
476 }
477
478 // ============================================================================
479 // UI CONSTRUCTION
480 // ============================================================================
481
482 function buildSidebarUI() {
483 console.log('🎨 Building Sidebar UI...');
484
485 const sidebar = document.createElement('div');
486 sidebar.className = 'solvaria-sidebar';
487 sidebar.innerHTML = `
488 <div class="solvaria-header">
489 <div class="solvaria-logo">
490 <div class="solvaria-logo-icon">⚡</div>
491 <div class="solvaria-logo-text">Solvaria Sidekick</div>
492 </div>
493 <button class="solvaria-close-btn" id="solvaria-close">✕</button>
494 </div>
495 <div class="solvaria-content" id="solvaria-content">
496 <div class="analysis-card">
497 <div class="card-header">
498 <div class="card-title">🔍 Analyzing CRM Context...</div>
499 </div>
500 <div class="card-content">
501 <div class="loading-spinner"></div> Connecting to Solvaria AI...
502 </div>
503 </div>
504 </div>
505 <div class="solvaria-footer">
506 <div class="roi-display">
507 <div class="roi-label">Estimated Time Saved</div>
508 <div class="roi-value">14 min</div>
509 </div>
510 </div>
511 `;
512
513 STATE.sidebarElement = sidebar;
514 return sidebar;
515 }
516
517 function buildGlowTrigger() {
518 console.log('✨ Building Glow Trigger...');
519
520 const glow = document.createElement('div');
521 glow.className = 'solvaria-glow';
522 glow.style.display = 'none';
523 glow.title = 'Click to open Solvaria Sidekick';
524
525 STATE.glowElement = glow;
526 return glow;
527 }
528
529 // ============================================================================
530 // GHOST-TO-COCKPIT BEHAVIOR
531 // ============================================================================
532
533 function showGlowTrigger(priorityScore) {
534 if (priorityScore > CONFIG.PRIORITY_THRESHOLD && STATE.glowElement) {
535 console.log(`🌟 Priority score ${priorityScore} > ${CONFIG.PRIORITY_THRESHOLD} - Showing glow trigger`);
536 STATE.glowElement.style.display = 'block';
537 }
538 }
539
540 function expandSidebar() {
541 if (STATE.isExpanded) return;
542
543 console.log('🚀 Expanding Sidebar - Ghost to Cockpit transition...');
544 STATE.isExpanded = true;
545
546 // Hide glow trigger
547 if (STATE.glowElement) {
548 STATE.glowElement.style.display = 'none';
549 }
550
551 // Apply margin to HTML element for smooth layout shift
552 const htmlElement = document.documentElement;
553 htmlElement.style.transition = `margin-right ${CONFIG.ANIMATION_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1)`;
554 htmlElement.style.marginRight = `${CONFIG.SIDEBAR_WIDTH}px`;
555
556 // Expand sidebar
557 if (STATE.sidebarElement) {
558 STATE.sidebarElement.classList.add('expanded');
559 }
560
561 // Trigger analysis
562 setTimeout(() => performCRMAnalysis(), 300);
563 }
564
565 function collapseSidebar() {
566 if (!STATE.isExpanded) return;
567
568 console.log('📦 Collapsing Sidebar - Cockpit to Ghost transition...');
569 STATE.isExpanded = false;
570
571 // Remove margin from HTML element
572 const htmlElement = document.documentElement;
573 htmlElement.style.marginRight = '0';
574
575 // Collapse sidebar
576 if (STATE.sidebarElement) {
577 STATE.sidebarElement.classList.remove('expanded');
578 }
579
580 // Show glow trigger again if priority is high
581 if (STATE.priorityScore > CONFIG.PRIORITY_THRESHOLD && STATE.glowElement) {
582 setTimeout(() => {
583 STATE.glowElement.style.display = 'block';
584 }, CONFIG.ANIMATION_DURATION);
585 }
586 }
587
588 // ============================================================================
589 // CRM ANALYSIS ENGINE
590 // ============================================================================
591
592 async function performCRMAnalysis() {
593 console.log('🔬 Performing CRM Analysis...');
594
595 const contentArea = STATE.shadowRoot.getElementById('solvaria-content');
596 if (!contentArea) return;
597
598 try {
599 // Extract CRM context
600 const crmContext = extractCRMContext();
601 console.log('📊 CRM Context extracted:', crmContext);
602
603 // Simulate backend call (replace with actual API)
604 const analysisResult = await simulateBackendAnalysis(crmContext);
605
606 // Update UI with results
607 displayAnalysisResults(analysisResult);
608
609 // Apply governance if needed
610 if (analysisResult.blockSubmit) {
611 applyTitaniumGovernance(analysisResult.blockReason);
612 }
613
614 } catch (error) {
615 console.error('❌ Analysis failed:', error);
616 contentArea.innerHTML = `
617 <div class="analysis-card">
618 <div class="card-header">
619 <div class="card-title">⚠️ Analysis Error</div>
620 </div>
621 <div class="card-content">
622 Unable to connect to Solvaria backend. Please check your connection.
623 </div>
624 </div>
625 `;
626 }
627 }
628
629 function extractCRMContext() {
630 const platform = detectCRMPlatform();
631 const context = {
632 platform: platform,
633 url: window.location.href,
634 title: document.title,
635 timestamp: new Date().toISOString(),
636 fields: []
637 };
638
639 // Extract form fields
640 const inputs = document.querySelectorAll('input, textarea, select');
641 inputs.forEach(input => {
642 if (input.value && input.name) {
643 context.fields.push({
644 name: input.name,
645 value: input.value,
646 type: input.type || input.tagName.toLowerCase()
647 });
648 }
649 });
650
651 return context;
652 }
653
654 async function simulateBackendAnalysis(context) {
655 // Simulate API delay
656 await new Promise(resolve => setTimeout(resolve, 1500));
657
658 // Mock response - replace with actual GM.xmlhttpRequest call
659 const priorityScore = Math.floor(Math.random() * 100);
660 STATE.priorityScore = priorityScore;
661
662 return {
663 priorityScore: priorityScore,
664 insights: [
665 {
666 title: 'Customer Risk Assessment',
667 content: 'Customer shows high engagement score. Contact information: <span class="sensitive-data">john.doe@example.com</span>',
668 priority: priorityScore > 70 ? 'high' : priorityScore > 40 ? 'medium' : 'low'
669 },
670 {
671 title: 'Recommended Actions',
672 content: 'Suggest premium tier upgrade. Estimated value: <span class="sensitive-data">$2,450/month</span>',
673 priority: 'medium'
674 },
675 {
676 title: 'Compliance Check',
677 content: 'All GDPR requirements satisfied. Data retention policy compliant.',
678 priority: 'low'
679 }
680 ],
681 blockSubmit: priorityScore > 80,
682 blockReason: 'Critical procedure verification required: Gas pressure check not completed',
683 suggestedResponse: 'Based on the customer profile and interaction history, I recommend...',
684 estimatedTimeSaved: 14
685 };
686 }
687
688 function displayAnalysisResults(results) {
689 const contentArea = STATE.shadowRoot.getElementById('solvaria-content');
690 if (!contentArea) return;
691
692 let html = '';
693
694 // Governance alert if blocking
695 if (results.blockSubmit) {
696 html += `
697 <div class="governance-alert">
698 <div class="governance-alert-title">
699 🔒 TITANIUM GOVERNANCE ACTIVE
700 </div>
701 <div class="governance-alert-message">
702 ${results.blockReason}
703 </div>
704 <button class="action-button" onclick="this.getRootNode().host.dispatchEvent(new CustomEvent('solvaria-unlock'))">
705 ✓ Verify & Unlock
706 </button>
707 </div>
708 `;
709 }
710
711 // Insights cards
712 results.insights.forEach(insight => {
713 html += `
714 <div class="analysis-card">
715 <div class="card-header">
716 <div class="card-title">${insight.title}</div>
717 <div class="priority-badge priority-${insight.priority}">
718 ${insight.priority}
719 </div>
720 </div>
721 <div class="card-content">${insight.content}</div>
722 </div>
723 `;
724 });
725
726 // AI Suggested Response
727 if (results.suggestedResponse) {
728 html += `
729 <div class="analysis-card">
730 <div class="card-header">
731 <div class="card-title">🤖 AI Suggested Response</div>
732 </div>
733 <div class="card-content">
734 <div class="streaming-response" id="streaming-response"></div>
735 </div>
736 <button class="action-button" id="inject-response">
737 ⚡ Inject Response
738 </button>
739 <button class="action-button secondary" id="copy-response">
740 📋 Copy to Clipboard
741 </button>
742 </div>
743 `;
744 }
745
746 contentArea.innerHTML = html;
747
748 // Start streaming animation
749 if (results.suggestedResponse) {
750 streamTextAnimation(results.suggestedResponse);
751 }
752
753 // Attach event listeners
754 attachEventListeners();
755 }
756
757 function streamTextAnimation(text) {
758 const streamingElement = STATE.shadowRoot.getElementById('streaming-response');
759 if (!streamingElement) return;
760
761 let index = 0;
762 const words = text.split(' ');
763
764 streamingElement.innerHTML = '<span class="typing-cursor"></span>';
765
766 const interval = setInterval(() => {
767 if (index < words.length) {
768 const cursor = streamingElement.querySelector('.typing-cursor');
769 const textNode = document.createTextNode(words[index] + ' ');
770 streamingElement.insertBefore(textNode, cursor);
771 index++;
772 } else {
773 clearInterval(interval);
774 const cursor = streamingElement.querySelector('.typing-cursor');
775 if (cursor) cursor.remove();
776 }
777 }, 80);
778 }
779
780 // ============================================================================
781 // TITANIUM GOVERNANCE (BLOCKING SYSTEM)
782 // ============================================================================
783
784 function applyTitaniumGovernance(reason) {
785 console.log('🔒 Applying Titanium Governance - Blocking submit buttons');
786
787 const buttons = getCRMSubmitButtons();
788 buttons.forEach(button => {
789 if (!STATE.blockedButtons.has(button)) {
790 // Store original styles
791 button.dataset.originalPointerEvents = button.style.pointerEvents || '';
792 button.dataset.originalOpacity = button.style.opacity || '';
793 button.dataset.originalFilter = button.style.filter || '';
794
795 // Apply blocking styles
796 button.style.pointerEvents = 'none';
797 button.style.opacity = '0.5';
798 button.style.filter = 'grayscale(1)';
799 button.style.transition = 'all 0.3s ease';
800
801 STATE.blockedButtons.add(button);
802 console.log('🚫 Blocked button:', button);
803 }
804 });
805 }
806
807 function removeTitaniumGovernance() {
808 console.log('🔓 Removing Titanium Governance - Unlocking submit buttons');
809
810 STATE.blockedButtons.forEach(button => {
811 // Restore original styles
812 button.style.pointerEvents = button.dataset.originalPointerEvents || '';
813 button.style.opacity = button.dataset.originalOpacity || '';
814 button.style.filter = button.dataset.originalFilter || '';
815
816 delete button.dataset.originalPointerEvents;
817 delete button.dataset.originalOpacity;
818 delete button.dataset.originalFilter;
819
820 console.log('✅ Unlocked button:', button);
821 });
822
823 STATE.blockedButtons.clear();
824 }
825
826 // ============================================================================
827 // INJECTION ENGINE
828 // ============================================================================
829
830 async function injectResponseToField() {
831 console.log('💉 Injecting AI response to CRM field...');
832
833 const streamingElement = STATE.shadowRoot.getElementById('streaming-response');
834 if (!streamingElement) return;
835
836 const responseText = streamingElement.textContent.trim();
837
838 // Find target field (textarea or rich text editor)
839 const targetField = findTargetInputField();
840
841 if (targetField) {
842 try {
843 // Focus the field
844 targetField.focus();
845
846 // Try execCommand first (works with rich text editors)
847 if (document.execCommand) {
848 targetField.select();
849 const success = document.execCommand('insertText', false, responseText);
850
851 if (success) {
852 console.log('✅ Injected using execCommand');
853 // Dispatch input event
854 targetField.dispatchEvent(new Event('input', { bubbles: true }));
855 targetField.dispatchEvent(new Event('change', { bubbles: true }));
856 showNotification('✓ Response injected successfully', 'success');
857 return;
858 }
859 }
860
861 // Fallback: direct value assignment
862 targetField.value = responseText;
863 targetField.dispatchEvent(new Event('input', { bubbles: true }));
864 targetField.dispatchEvent(new Event('change', { bubbles: true }));
865 console.log('✅ Injected using direct assignment');
866 showNotification('✓ Response injected successfully', 'success');
867
868 } catch (error) {
869 console.error('❌ Injection failed:', error);
870 showNotification('⚠ Injection failed - Use copy button', 'warning');
871 }
872 } else {
873 console.warn('⚠ No target field found');
874 showNotification('⚠ No input field found - Use copy button', 'warning');
875 }
876 }
877
878 function findTargetInputField() {
879 // Try to find the most relevant input field
880 const selectors = [
881 'textarea[name*="comment"]',
882 'textarea[name*="description"]',
883 'textarea[name*="notes"]',
884 'textarea[name*="message"]',
885 'div[contenteditable="true"]',
886 'textarea:focus',
887 'textarea'
888 ];
889
890 for (const selector of selectors) {
891 const field = document.querySelector(selector);
892 if (field && field.offsetParent !== null) { // Check if visible
893 return field;
894 }
895 }
896
897 return null;
898 }
899
900 async function copyResponseToClipboard() {
901 const streamingElement = STATE.shadowRoot.getElementById('streaming-response');
902 if (!streamingElement) return;
903
904 const responseText = streamingElement.textContent.trim();
905
906 try {
907 await GM.setClipboard(responseText);
908 console.log('📋 Copied to clipboard');
909 showNotification('✓ Copied to clipboard', 'success');
910 } catch (error) {
911 console.error('❌ Copy failed:', error);
912 showNotification('⚠ Copy failed', 'warning');
913 }
914 }
915
916 function showNotification(message, type = 'info') {
917 const notification = document.createElement('div');
918 notification.style.cssText = `
919 position: fixed;
920 top: 20px;
921 right: ${STATE.isExpanded ? CONFIG.SIDEBAR_WIDTH + 20 : 20}px;
922 background: ${type === 'success' ? CONFIG.COLORS.success : type === 'warning' ? CONFIG.COLORS.warning : CONFIG.COLORS.accent};
923 color: white;
924 padding: 12px 20px;
925 border-radius: 8px;
926 font-size: 14px;
927 font-weight: 600;
928 z-index: ${CONFIG.Z_INDEX_MAX + 1};
929 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
930 animation: slideIn 0.3s ease;
931 `;
932 notification.textContent = message;
933
934 document.body.appendChild(notification);
935
936 setTimeout(() => {
937 notification.style.animation = 'slideOut 0.3s ease';
938 setTimeout(() => notification.remove(), 300);
939 }, 3000);
940 }
941
942 // ============================================================================
943 // EVENT HANDLERS
944 // ============================================================================
945
946 function attachEventListeners() {
947 // Close button
948 const closeBtn = STATE.shadowRoot.getElementById('solvaria-close');
949 if (closeBtn) {
950 closeBtn.addEventListener('click', collapseSidebar);
951 }
952
953 // Inject button
954 const injectBtn = STATE.shadowRoot.getElementById('inject-response');
955 if (injectBtn) {
956 injectBtn.addEventListener('click', injectResponseToField);
957 }
958
959 // Copy button
960 const copyBtn = STATE.shadowRoot.getElementById('copy-response');
961 if (copyBtn) {
962 copyBtn.addEventListener('click', copyResponseToClipboard);
963 }
964
965 // Unlock governance button (via custom event)
966 const host = STATE.shadowRoot.host;
967 host.addEventListener('solvaria-unlock', () => {
968 removeTitaniumGovernance();
969 showNotification('✓ Governance unlocked', 'success');
970
971 // Update UI to remove alert
972 const alert = STATE.shadowRoot.querySelector('.governance-alert');
973 if (alert) {
974 alert.style.animation = 'slideOut 0.3s ease';
975 setTimeout(() => alert.remove(), 300);
976 }
977 });
978 }
979
980 // ============================================================================
981 // INITIALIZATION
982 // ============================================================================
983
984 async function init() {
985 console.log('🎯 Initializing Solvaria Sidekick...');
986
987 // Wait for body to be ready
988 if (!document.body) {
989 console.log('⏳ Waiting for document.body...');
990 await new Promise(resolve => {
991 if (document.readyState === 'loading') {
992 document.addEventListener('DOMContentLoaded', resolve);
993 } else {
994 setTimeout(resolve, 100);
995 }
996 });
997 }
998
999 // Create Shadow DOM container
1000 const { host, shadowRoot } = createShadowDOMContainer();
1001
1002 // Build UI components
1003 const glow = buildGlowTrigger();
1004 const sidebar = buildSidebarUI();
1005
1006 // Append to shadow root
1007 shadowRoot.appendChild(glow);
1008 shadowRoot.appendChild(sidebar);
1009
1010 // Attach glow click handler
1011 glow.addEventListener('click', expandSidebar);
1012
1013 // Monitor CRM changes for auto-trigger
1014 observeCRMChanges();
1015
1016 // Simulate priority check (replace with actual backend call)
1017 setTimeout(() => {
1018 const mockPriorityScore = Math.floor(Math.random() * 100);
1019 STATE.priorityScore = mockPriorityScore;
1020 showGlowTrigger(mockPriorityScore);
1021 }, 2000);
1022
1023 console.log('✅ Solvaria Sidekick initialized successfully');
1024 }
1025
1026 function observeCRMChanges() {
1027 // Debounced observer for CRM form changes
1028 const debouncedAnalysis = debounce(() => {
1029 if (STATE.isExpanded) {
1030 console.log('🔄 CRM context changed - Re-analyzing...');
1031 performCRMAnalysis();
1032 }
1033 }, 1000);
1034
1035 const observer = new MutationObserver(debouncedAnalysis);
1036
1037 observer.observe(document.body, {
1038 childList: true,
1039 subtree: true,
1040 attributes: false
1041 });
1042
1043 console.log('👀 Observing CRM changes...');
1044 }
1045
1046 // ============================================================================
1047 // START APPLICATION
1048 // ============================================================================
1049
1050 // Initialize when DOM is ready
1051 if (document.readyState === 'loading') {
1052 document.addEventListener('DOMContentLoaded', init);
1053 } else {
1054 init();
1055 }
1056
1057})();