ccx / index.html
jjmandog's picture
I own a car detailing business. Teach ai about it - Follow Up Deployment
fd46987 verified
raw
history blame
55.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Phone Assistant</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/@rvcjs/core@latest/dist/rvc.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@okada-tts/web@latest/dist/okada.min.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
iosbg: '#f2f2f7',
iosdark: '#1c1c1e',
accent: '#0a84ff',
accent2: '#5e5ce6',
}
}
}
}
</script>
<style>
/* iOS-like transitions and scrolling */
body {
-webkit-tap-highlight-color: transparent;
-webkit-overflow-scrolling: touch;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.screen {
display: none;
opacity: 0;
transition: opacity 0.3s ease;
}
.screen.active {
display: block;
opacity: 1;
}
/* Custom iOS-like scrollbars */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.15);
border-radius: 3px;
}
/* iOS-like switch */
.ios-switch {
position: relative;
display: inline-block;
width: 52px;
height: 32px;
}
.ios-switch input {
opacity: 0;
width: 0;
height: 0;
}
.ios-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #e9e9ea;
transition: .4s;
border-radius: 16px;
}
.ios-slider:before {
position: absolute;
content: "";
height: 28px;
width: 28px;
left: 2px;
bottom: 2px;
background-color: white;
transition: .4s;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
input:checked + .ios-slider {
background-color: #32d74b;
}
input:checked + .ios-slider:before {
transform: translateX(20px);
}
/* Floating action button */
.fab {
position: fixed;
right: 20px;
bottom: 20px;
width: 60px;
height: 60px;
background: linear-gradient(135deg, #0a84ff, #5e5ce6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 22px;
box-shadow: 0 4px 10px rgba(0,0,0,0.25);
z-index: 50;
cursor: pointer;
}
/* Custom inputs */
.ios-input {
background: rgba(118,118,128,0.12);
border-radius: 10px;
padding: 8px 12px;
font-size: 16px;
width: 100%;
transition: background 0.2s;
}
.ios-input:focus {
background: rgba(118,118,128,0.18);
outline: none;
}
/* Chat bubbles */
.ai-bubble {
background: rgba(118,118,128,0.12);
border-top-left-radius: 18px;
border-top-right-radius: 18px;
border-bottom-right-radius: 18px;
padding: 12px 16px;
max-width: 85%;
align-self: flex-start;
}
.user-bubble {
background: #0a84ff;
color: white;
border-top-left-radius: 18px;
border-top-right-radius: 18px;
border-bottom-left-radius: 18px;
padding: 12px 16px;
max-width: 85%;
align-self: flex-end;
}
/* Dynamic animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fadeIn {
animation: fadeIn 0.3s ease-out forwards;
}
</style>
</head>
<body class="bg-iosbg dark:bg-iosdark text-gray-900 dark:text-gray-200 min-h-screen">
<!-- Status Bar -->
<div class="fixed top-0 left-0 right-0 h-12 flex items-center px-4 z-50 bg-iosbg dark:bg-iosdark">
<div class="text-left text-sm w-20">9:41</div>
<div class="flex-1 flex justify-center">
<i class="fas fa-signal mr-2"></i>
<i class="fas fa-wifi mr-2"></i>
<i class="fas fa-battery-three-quarters"></i>
</div>
<div class="w-20 text-right text-xs">100%</div>
</div>
<!-- Main App Container -->
<div class="relative pt-12 max-w-md mx-auto h-screen overflow-hidden">
<!-- Home Screen -->
<div id="homeScreen" class="screen active px-4 pt-4 h-full flex flex-col">
<div class="mt-2">
<h1 class="text-3xl font-bold">Call Assistant</h1>
<p class="text-gray-500 dark:text-gray-400 mt-1">AI that answers your calls and learns over time</p>
</div>
<!-- Status Card -->
<div class="mt-6 bg-white dark:bg-gray-800 rounded-2xl p-5 shadow-sm">
<div class="flex items-center justify-between mb-4">
<div>
<h2 class="font-medium">Current Status</h2>
<p class="text-gray-500 dark:text-gray-400 text-sm mt-1">Assistant is active</p>
</div>
<label class="ios-switch">
<input type="checkbox" checked>
<span class="ios-slider"></span>
</label>
</div>
<div class="border-t border-gray-200 dark:border-gray-700 pt-4">
<h3 class="font-medium flex items-center">
<i class="fas fa-phone mr-2"></i> Connected Number
</h3>
<div class="mt-2 flex items-center justify-between">
<span class="text-gray-500 dark:text-gray-400">+1 (562) 228-9429</span>
<button class="text-accent text-sm">Change</button>
</div>
</div>
<div class="mt-4 flex items-center">
<div class="w-10 h-10 rounded-full bg-accent flex items-center justify-center">
<i class="fas fa-robot text-white"></i>
</div>
<div class="ml-3">
<h3 class="font-medium">Today's Stats</h3>
<p class="text-gray-500 dark:text-gray-400 text-sm">Answered 5 calls, 12 min talk time</p>
</div>
</div>
</div>
<!-- Action Cards -->
<div class="mt-4 flex gap-3">
<div class="flex-1 bg-gradient-to-br from-accent to-accent2 rounded-2xl p-5 text-white">
<i class="fas fa-comment-alt text-2xl"></i>
<h3 class="font-medium mt-3">Smart Replies</h3>
<p class="text-white text-opacity-80 text-sm mt-1">Teach the AI how to respond</p>
</div>
<div class="flex-1 bg-gray-800 dark:bg-gray-700 rounded-2xl p-5 text-white">
<i class="fas fa-history text-2xl"></i>
<h3 class="font-medium mt-3">Call History</h3>
<p class="text-gray-300 text-sm mt-1">Review previous conversations</p>
</div>
</div>
<!-- Recent Activity -->
<div class="mt-4 bg-white dark:bg-gray-800 rounded-2xl p-5 flex-1 overflow-hidden flex flex-col">
<div class="flex items-center justify-between">
<h2 class="font-medium">Recent Activity</h2>
<button class="text-accent text-sm">See All</button>
</div>
<div class="mt-3 flex-1 overflow-y-auto space-y-4">
<div class="flex items-start animate-fadeIn">
<div class="w-10 h-10 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center">
<i class="fas fa-phone-alt text-green-600 dark:text-green-400"></i>
</div>
<div class="ml-3 flex-1">
<div class="flex justify-between">
<h3 class="font-medium">Michael (Work)</h3>
<span class="text-xs text-gray-500 dark:text-gray-400">12:45 PM</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">AI Assistant handled call: "Meeting confirmed for tomorrow"</p>
</div>
</div>
<div class="flex items-start animate-fadeIn">
<div class="w-10 h-10 rounded-full bg-purple-100 dark:bg-purple-900 flex items-center justify-center">
<i class="fas fa-phone-alt text-purple-600 dark:text-purple-400"></i>
</div>
<div class="ml-3 flex-1">
<div class="flex justify-between">
<h3 class="font-medium">Sarah (Spam)</h3>
<span class="text-xs text-gray-500 dark:text-gray-400">11:30 AM</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">AI blocked suspected spam call</p>
</div>
</div>
<div class="flex items-start animate-fadeIn">
<div class="w-10 h-10 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center">
<i class="fas fa-phone-alt text-blue-600 dark:text-blue-400"></i>
</div>
<div class="ml-3 flex-1">
<div class="flex justify-between">
<h3 class="font-medium">Mom</h3>
<span class="text-xs text-gray-500 dark:text-gray-400">10:15 AM</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">AI transferred call to you after screening</p>
</div>
</div>
<div class="flex items-start animate-fadeIn">
<div class="w-10 h-10 rounded-full bg-amber-100 dark:bg-amber-900 flex items-center justify-center">
<i class="fas fa-phone-alt text-amber-600 dark:text-amber-400"></i>
</div>
<div class="ml-3 flex-1">
<div class="flex justify-between">
<h3 class="font-medium">Dr. Smith Clinic</h3>
<span class="text-xs text-gray-500 dark:text-gray-400">9:20 AM</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">AI scheduled your appointment for next Monday</p>
</div>
</div>
</div>
</div>
</div>
<!-- Configure Replies Screen -->
<div id="repliesScreen" class="screen h-full flex flex-col">
<div class="px-4 pt-4">
<div class="flex items-center">
<button class="p-2 rounded-full" onclick="showScreen('homeScreen')">
<i class="fas fa-arrow-left"></i>
</button>
<h2 class="text-xl font-bold ml-2">Smart Replies</h2>
</div>
<p class="text-gray-500 dark:text-gray-400 mt-1 ml-12">Customize how AI answers calls</p>
</div>
<div class="mt-4 px-4 flex-1 overflow-y-auto">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5 mb-4">
<div class="flex items-center">
<div class="w-12 h-12 rounded-full bg-gradient-to-br from-accent to-accent2 flex items-center justify-center">
<i class="fas fa-brain text-white text-xl"></i>
</div>
<div class="ml-3">
<h3 class="font-medium">AI Learning Mode</h3>
<p class="text-gray-500 dark:text-gray-400 text-sm">Improves responses over time</p>
</div>
</div>
<div class="mt-4 flex items-center justify-between">
<span>Learning from interactions</span>
<label class="ios-switch">
<input type="checkbox" checked>
<span class="ios-slider"></span>
</label>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-2xl overflow-hidden">
<div class="px-5 pt-4">
<h3 class="font-medium">Custom Response Templates</h3>
<p class="text-gray-500 dark:text-gray-400 text-sm mt-1">Set predefined responses</p>
</div>
<div class="mt-4 space-y-2">
<div class="flex items-center justify-between p-4 hover:bg-gray-100 dark:hover:bg-gray-750 cursor-pointer">
<div>
<h4 class="font-medium">Business Calls</h4>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">"Hello, this is [Your Name]'s assistant..."</p>
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
<div class="flex items-center justify-between p-4 hover:bg-gray-100 dark:hover:bg-gray-750 cursor-pointer">
<div>
<h4 class="font-medium">Personal Calls</h4>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">"Hi, this is [Name]'s phone..."</p>
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
<div class="flex items-center justify-between p-4 hover:bg-gray-100 dark:hover:bg-gray-750 cursor-pointer">
<div>
<h4 class="font-medium">Spam Protection</h4>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">"Sorry, this number is not accepting calls..."</p>
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-2xl mt-4 p-5">
<h3 class="font-medium">Response Style</h3>
<div class="mt-4 space-y-4">
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Formal Tone</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Professional business language</p>
</div>
<label class="ios-switch">
<input type="checkbox" checked>
<span class="ios-slider"></span>
</label>
</div>
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Friendly Tone</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Casual conversation style</p>
</div>
<label class="ios-switch">
<input type="checkbox">
<span class="ios-slider"></span>
</label>
</div>
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Use My Name</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">"This is [Your Name]'s phone"</p>
</div>
<label class="ios-switch">
<input type="checkbox" checked>
<span class="ios-slider"></span>
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Call Training Screen -->
<div id="trainingScreen" class="screen h-full flex flex-col">
<div class="px-4 pt-4">
<div class="flex items-center">
<button class="p-2 rounded-full" onclick="showScreen('homeScreen')">
<i class="fas fa-arrow-left"></i>
</button>
<h2 class="text-xl font-bold ml-2">Train AI</h2>
</div>
<p class="text-gray-500 dark:text-gray-400 mt-1 ml-12">Help your assistant learn</p>
</div>
<div class="mt-4 px-4 flex-1 overflow-hidden flex flex-col">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5 flex-1 overflow-hidden flex flex-col">
<div class="flex-1 overflow-y-auto pb-4">
<div class="ai-bubble">
<p>How would you like me to respond to calls from your family?</p>
</div>
<div class="user-bubble mt-4">
<p>Always transfer calls from Mom and Dad to me</p>
</div>
<div class="ai-bubble mt-4">
<p>Noted! I'll transfer calls from Mom and Dad immediately.</p>
<p class="mt-2">For other family members, how should I respond?</p>
</div>
<div class="user-bubble mt-4">
<p>Ask them for the reason of calling and text me if it's important</p>
</div>
<div class="ai-bubble mt-4">
<p>Got it. Here's the response I created based on your feedback:</p>
<div class="mt-2 bg-blue-50 dark:bg-blue-900 rounded-lg p-3">
<p>"Hello, this is Alex's assistant. Could you let me know what you're calling about? I'll make sure they get your message."</p>
</div>
<p class="mt-2">Does this work?</p>
</div>
</div>
<div class="mt-auto pt-4 border-t border-gray-200 dark:border-gray-700">
<div class="flex gap-2">
<input id="trainingInput" type="text" class="ios-input flex-1" placeholder="Teach your assistant..." onkeypress="handleTrainingKeyPress(event)">
<button class="w-12 h-12 rounded-xl bg-accent flex items-center justify-center text-white" onclick="submitTraining()">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2 text-center">The AI learns from every interaction</p>
</div>
</div>
</div>
</div>
<!-- Tab Bar -->
<div class="fixed bottom-0 left-0 right-0 bg-white dark:bg-iosdark border-t border-gray-200 dark:border-gray-800 max-w-md mx-auto">
<div class="flex justify-around py-2">
<button class="py-2 px-4 rounded-xl flex flex-col items-center text-accent" onclick="showScreen('homeScreen')">
<i class="fas fa-home text-lg"></i>
<span class="text-xs mt-1">Home</span>
</button>
<button class="py-2 px-4 rounded-xl flex flex-col items-center text-gray-500" onclick="showScreen('repliesScreen')">
<i class="fas fa-comment-alt text-lg"></i>
<span class="text-xs mt-1">Replies</span>
</button>
<button class="py-2 px-4 rounded-xl flex flex-col items-center text-gray-500" onclick="showScreen('trainingScreen')">
<i class="fas fa-graduation-cap text-lg"></i>
<span class="text-xs mt-1">Train</span>
</button>
<button class="py-2 px-4 rounded-xl flex flex-col items-center text-gray-500" onclick="showScreen('settingsScreen')">
<i class="fas fa-cog text-lg"></i>
<span class="text-xs mt-1">Settings</span>
</button>
</div>
</div>
<!-- Settings Screen -->
<div id="settingsScreen" class="screen h-full flex flex-col">
<div class="px-4 pt-4">
<div class="flex items-center">
<button class="p-2 rounded-full" onclick="showScreen('homeScreen')">
<i class="fas fa-arrow-left"></i>
</button>
<h2 class="text-xl font-bold ml-2">Voice Settings</h2>
</div>
<p class="text-gray-500 dark:text-gray-400 mt-1 ml-12">Customize voice personality</p>
</div>
<div class="mt-4 px-4 flex-1 overflow-y-auto">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5">
<h3 class="font-medium">Voice Personality</h3>
<div class="mt-4 space-y-4">
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Voice Model</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">RVC-based human voice</p>
</div>
<select id="voiceModel" class="ios-input">
<option value="okada-female">OKADA Female</option>
<option value="okada-male">OKADA Male</option>
<option value="custom-rvc">Custom RVC</option>
</select>
</div>
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Emotional Tone</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Adjust voice emotions</p>
</div>
<select id="voiceTone" class="ios-input">
<option value="neutral">Neutral</option>
<option value="friendly">Friendly</option>
<option value="professional">Professional</option>
<option value="empathic">Empathic</option>
</select>
</div>
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Speed</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Speech rate</p>
</div>
<select id="voiceSpeed" class="ios-input">
<option value="1.0">Normal</option>
<option value="0.8">Slow</option>
<option value="1.2">Fast</option>
</select>
</div>
<div class="mt-6">
<button class="w-full py-3 bg-accent text-white rounded-xl" onclick="testVoiceSettings()">
Test Voice Settings
</button>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-2xl mt-4 p-5">
<h3 class="font-medium">Advanced RVC Settings</h3>
<div class="mt-4 space-y-2">
<div>
<label class="text-sm text-gray-500 dark:text-gray-400">Pitch Shift</label>
<input type="range" id="pitchShift" min="-12" max="12" value="0" step="1" class="w-full mt-1">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>Lower</span>
<span>Normal</span>
<span>Higher</span>
</div>
</div>
<div class="mt-4">
<div class="flex justify-between">
<label class="text-sm">RVC Similarity</label>
<span id="similarityValue" class="text-sm">85%</span>
</div>
<input type="range" id="similarity" min="50" max="100" value="85" step="1" class="w-full mt-1">
</div>
</div>
</div>
</div>
</div>
<!-- Floating Action Button -->
<div class="fab" onclick="toggleRecording()">
<i class="fas fa-microphone"></i>
</div>
</div>
<script>
// Enhanced AI Training Data with Natural Language Learning
let trainingData = {
learnedResponses: {
"business": [
{
"input": "car detailing",
"response": "Thank you for calling [Your Business Name], premium auto detailing specialists. How can we help you today?",
"tokens": ["car", "detailing"],
"lastUsed": "2023-11-15T00:00:00.000Z"
},
{
"input": "appointment",
"response": "For appointments, we have availability this week. Would you prefer a wash & wax ($75), full detail ($150) or ceramic coating ($400)?",
"tokens": ["appointment"],
"lastUsed": "2023-11-15T00:00:00.000Z"
},
{
"input": "hours",
"response": "Our detailing center is open Monday-Friday 8am-6pm, Saturday 9am-4pm. We're closed Sundays.",
"tokens": ["hours"],
"lastUsed": "2023-11-15T00:00:00.000Z"
}
]
},
customResponses: {
"business": "Hello, you've reached [Your Business Name] auto detailing. Ask me about our services: basic wash ($35), premium detailing ($150), or ceramic coatings ($400+).",
"spam": "We don't accept sales calls. Please email us at [email protected] for business inquiries."
},
phoneNumber: "+1 (562) 228-9429",
callHandling: {
transferContacts: ["Mom", "Dad"],
screenContacts: true,
spamDetection: true
},
personality: {
tone: "professional",
useName: true,
responseSpeed: "normal",
businessName: "Shine On Auto Detailing",
services: [
{name: "Basic Wash", price: "$35", duration: "30 mins"},
{name: "Deluxe Detail", price: "$150", duration: "3 hours"},
{name: "Ceramic Coating", price: "$400+", duration: "1-2 days"}
]
}
};
// Initialize Natural Language Processing
const nlp = {
processInput: function(text) {
// Simple NLP processing
text = text.toLowerCase().trim();
const keywords = {
business: ["work", "job", "meeting", "business"],
personal: ["family", "friend", "mom", "dad"],
spam: ["spam", "block", "unwanted", "telemarketer"],
question: ["what", "when", "where", "how", "why", "can you", "would you"]
};
return text;
},
learnResponse: function(inputText, preferredResponse) {
// Tokenize input and learn patterns
const tokens = inputText.toLowerCase().split(/\s+/);
const context = this.determineContext(inputText);
if (!trainingData.learnedResponses[context]) {
trainingData.learnedResponses[context] = [];
}
trainingData.learnedResponses[context].push({
input: inputText,
response: preferredResponse,
tokens: tokens,
lastUsed: new Date()
});
this.saveToLocalStorage();
return true;
},
determineContext: function(text) {
text = text.toLowerCase();
if (text.includes('work') || text.includes('business') || text.includes('job')) return 'business';
if (text.includes('family') || text.includes('mom') || text.includes('dad')) return 'personal';
if (text.includes('spam') || text.includes('block')) return 'spam';
return 'general';
},
getBestResponse: function(inputText) {
const context = this.determineContext(inputText);
const possibleResponses = trainingData.learnedResponses[context] || [];
if (possibleResponses.length === 0) {
return this.generateDefaultResponse(context);
}
// Find the most relevant response based on keyword matching
const inputTokens = inputText.toLowerCase().split(/\s+/);
let bestMatch = {score: 0, response: possibleResponses[0]};
possibleResponses.forEach(item => {
let score = 0;
inputTokens.forEach(token => {
if (item.tokens.includes(token)) score++;
});
if (score > bestMatch.score) {
bestMatch = {score, response: item};
}
});
return bestMatch.response.response || this.generateDefaultResponse(context);
},
generateDefaultResponse: function(context) {
const defaults = {
business: "Hello! Thank you for calling our auto detailing service. We offer car washes starting at $35 and full detailing for $150. How can we assist you?",
personal: "Hi, this is [Name]'s phone. I'll get your message to them if it's important.",
spam: "We don't accept sales calls. Please email us at [email protected] for business inquiries.",
general: "I'll connect you with our detailing team. Are you calling about an appointment or would you like service information?"
};
return defaults[context] || defaults.general;
},
saveToLocalStorage: function() {
localStorage.setItem('aiCallAssistantTraining', JSON.stringify(trainingData));
},
loadFromLocalStorage: function() {
const savedData = localStorage.getItem('aiCallAssistantTraining');
if (savedData) {
trainingData = JSON.parse(savedData);
}
}
};
// Screen navigation
function showScreen(screenId) {
document.querySelectorAll('.screen').forEach(screen => {
screen.classList.remove('active');
});
document.getElementById(screenId).classList.add('active');
// Update tab bar active state
const tabs = document.querySelectorAll('.fab ~ .fixed button');
tabs.forEach(tab => {
const icon = tab.querySelector('i');
const span = tab.querySelector('span');
if (screenId === 'homeScreen' && icon.classList.contains('fa-home')) {
tab.classList.add('text-accent');
} else if (screenId === 'repliesScreen' && icon.classList.contains('fa-comment-alt')) {
tab.classList.add('text-accent');
} else if (screenId === 'trainingScreen' && icon.classList.contains('fa-graduation-cap')) {
tab.classList.add('text-accent');
} else {
tab.classList.remove('text-accent');
tab.classList.add('text-gray-500');
}
});
}
// Simulate AI learning progress
function simulateAIProgress() {
const progressBars = document.querySelectorAll('.progress');
progressBars.forEach(bar => {
const width = 70 + Math.floor(Math.random() * 30);
bar.style.width = `${width}%`;
});
}
// Toggle call recording function
function toggleRecording() {
const fab = document.querySelector('.fab');
const icon = fab.querySelector('i');
if (icon.classList.contains('fa-microphone')) {
icon.classList.remove('fa-microphone');
icon.classList.add('fa-stop');
fab.style.background = 'linear-gradient(135deg, #ff375f, #ff2d55)';
// Show a notification
const notification = document.createElement('div');
notification.innerHTML = `
<div class="fixed top-16 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white px-4 py-2 rounded-full animate-fadeIn">
Recording started
</div>
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 2000);
} else {
icon.classList.remove('fa-stop');
icon.classList.add('fa-microphone');
fab.style.background = 'linear-gradient(135deg, #0a84ff, #5e5ce6)';
// Simulate AI learning after recording
simulateAIProgress();
}
}
// Add animation to elements when they appear
function addAppearAnimations() {
document.querySelectorAll('.animate-fadeIn').forEach((el, index) => {
el.style.animationDelay = `${index * 0.1}s`;
});
}
// Enhanced Training Functions
function submitTraining() {
const input = document.getElementById('trainingInput');
if (input.value.trim() !== '') {
const userMessage = input.value;
// Add user message to chat
const chatContainer = document.querySelector('#trainingScreen .flex-1.overflow-y-auto');
const userBubble = document.createElement('div');
userBubble.className = 'user-bubble mt-4 animate-fadeIn';
userBubble.innerHTML = `<p>${userMessage}</p>`;
chatContainer.appendChild(userBubble);
// Process user input and generate AI response
setTimeout(async () => {
const isTeachingExample = userMessage.includes("should respond");
let aiResponse;
if (isTeachingExample) {
// Extract teaching example
const parts = userMessage.split("should respond");
const question = parts[0].trim();
const answer = parts[1].replace(/with/i, '').trim();
// Teach the AI this response
nlp.learnResponse(question, answer);
aiResponse = `
<p>Got it! I've learned this response:</p>
<div class="mt-2 bg-blue-50 dark:bg-blue-900 rounded-lg p-3">
<p><strong>Question:</strong> ${question}</p>
<p><strong>Answer:</strong> ${answer}</p>
</div>
<p class="mt-2">I'll use this response when I hear similar questions.</p>
`;
} else {
// Get smart response based on learned data
const response = nlp.getBestResponse(userMessage);
aiResponse = `
<p>Based on what you've taught me, here's how I would respond:</p>
<div class="mt-2 bg-blue-50 dark:bg-blue-900 rounded-lg p-3">
<p>${response}</p>
</div>
`;
}
const aiBubble = document.createElement('div');
aiBubble.className = 'ai-bubble mt-4 animate-fadeIn';
aiBubble.innerHTML = aiResponse + `
<div class="mt-3 flex gap-2">
<button class="flex-1 py-2 bg-green-500 text-white rounded-lg" onclick="acceptResponse(this)">
Accept <i class="fas fa-check ml-1"></i>
</button>
<button class="flex-1 py-2 bg-red-500 text-white rounded-lg" onclick="editResponse(this)">
Edit <i class="fas fa-edit ml-1"></i>
</button>
<button class="px-3 py-2 bg-accent text-white rounded-lg" onclick="playResponse(this)">
<i class="fas fa-volume-up"></i>
</button>
</div>
`;
chatContainer.appendChild(aiBubble);
chatContainer.scrollTop = chatContainer.scrollHeight;
}, 800);
input.value = '';
}
}
function acceptResponse(button) {
const responseText = button.closest('.ai-bubble').querySelector('div p').textContent;
const context = nlp.determineContext(responseText);
trainingData.customResponses[context] = responseText;
nlp.saveToLocalStorage();
button.innerHTML = '<i class="fas fa-check-circle"></i> Saved';
button.classList.remove('bg-green-500');
button.classList.add('bg-emerald-600');
}
function editResponse(button) {
const bubble = button.closest('.ai-bubble');
const responseDiv = bubble.querySelector('div.bg-blue-50');
const currentText = responseDiv.querySelector('p').textContent;
responseDiv.innerHTML = `
<textarea class="w-full p-2 rounded border border-gray-300">${currentText}</textarea>
<button onclick="saveEditedResponse(this)" class="mt-2 px-4 py-1 bg-accent text-white rounded">
Save Changes
</button>
`;
}
function saveEditedResponse(button) {
const newText = button.parentElement.querySelector('textarea').value;
const bubble = button.closest('.ai-bubble');
const responseDiv = button.parentElement;
responseDiv.innerHTML = `<p>${newText}</p>`;
// Update the training data with edited response
const context = nlp.determineContext(newText);
trainingData.customResponses[context] = newText;
nlp.saveToLocalStorage();
}
function handleTrainingKeyPress(e) {
if (e.key === 'Enter') {
submitTraining();
}
}
// Voice Processing Functions
let voiceProcessor = {
model: 'okada-female',
tone: 'neutral',
speed: 1.0,
pitch: 0,
similarity: 0.85,
init: function() {
// Initialize OKADA TTS
this.tts = new OkadaTTS({
voice: this.model,
speed: this.speed,
pitch: this.pitch
});
// Initialize RVC
this.rvc = new RVC({
similarity: this.similarity,
pitchShift: this.pitch
});
},
speak: function(text) {
// Process text with OKADA TTS
const ttsResult = this.tts.synthesize(text, this.tone);
// Apply RVC voice conversion
return this.rvc.convert(ttsResult);
},
updateSettings: function() {
this.model = document.getElementById('voiceModel').value;
this.tone = document.getElementById('voiceTone').value;
this.speed = parseFloat(document.getElementById('voiceSpeed').value);
this.pitch = parseInt(document.getElementById('pitchShift').value);
this.similarity = parseInt(document.getElementById('similarity').value) / 100;
this.init();
}
};
function testVoiceSettings() {
voiceProcessor.updateSettings();
const audio = voiceProcessor.speak("Hello, this is your AI call assistant with new voice settings.");
audio.play();
}
// Update similarity display
document.getElementById('similarity').addEventListener('input', function() {
document.getElementById('similarityValue').textContent = this.value + '%';
});
function playResponse(button) {
const responseText = button.parentElement.querySelector('div p').textContent;
const audio = voiceProcessor.speak(responseText);
audio.play();
}
// Phone Integration with WebRTC
const phoneHandler = {
client: null,
isCallActive: false,
currentCall: null,
init: function() {
// Load saved training data
nlp.loadFromLocalStorage();
// Initialize WebRTC client (simplified)
this.client = {
onIncomingCall: (call) => this.handleIncomingCall(call),
answerCall: (call) => this.answerCall(call),
endCall: () => this.endCall()
};
// Check for active call on page load
this.checkActiveCall();
// Initialize voice processor
voiceProcessor.init();
addAppearAnimations();
},
handleIncomingCall: function(call) {
this.isCallActive = true;
this.currentCall = call;
// Update UI to show active call
showScreen('homeScreen');
document.querySelector('.fab i').classList.remove('fa-microphone');
document.querySelector('.fab i').classList.add('fa-phone-alt');
document.querySelector('.fab').style.background = '#ff375f';
// Show call notification
const callerInfo = call.callerID || 'Unknown Caller';
const notification = document.createElement('div');
notification.className = 'fixed top-16 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white px-4 py-2 rounded-full animate-fadeIn z-50';
notification.innerHTML = `
Incoming call from: ${callerInfo}
<div class="mt-2 flex justify-center gap-4">
<button onclick="phoneHandler.answerCall()" class="bg-green-500 w-12 h-12 rounded-full flex items-center justify-center">
<i class="fas fa-phone"></i>
</button>
<button onclick="phoneHandler.endCall()" class="bg-red-500 w-12 h-12 rounded-full flex items-center justify-center">
<i class="fas fa-phone-slash"></i>
</button>
</div>
`;
document.body.appendChild(notification);
// Auto-screen call if enabled
if (trainingData.callHandling.screenContacts ||
(!callerInfo && trainingData.callHandling.spamDetection)) {
setTimeout(() => this.screenCall(), 2000);
}
},
screenCall: function() {
const callerInfo = this.currentCall.callerID || 'Unknown Caller';
const isSpam = callerInfo === 'Unknown Caller' && trainingData.callHandling.spamDetection;
const isTransfer = trainingData.callHandling.transferContacts.some(name =>
callerInfo.includes(name));
let response;
if (isTransfer) {
response = `Transferring call from ${callerInfo} to you...`;
this.currentCall.transfer();
} else if (isSpam) {
response = trainingData.customResponses.spam || nlp.generateDefaultResponse('spam');
this.currentCall.reject();
} else {
response = nlp.getBestResponse(callerInfo);
this.currentCall.speakResponse(response);
}
// Log the interaction
this.logInteraction(callerInfo, response, isSpam ? 'blocked' : 'screened');
},
answerCall: function() {
if (!this.isCallActive) return;
const callerInfo = this.currentCall.callerID || 'Unknown Caller';
this.currentCall.answer();
// Generate smart greeting
const greeting = nlp.getBestResponse(`call from ${callerInfo}`);
this.currentCall.speakResponse(greeting);
// Setup voice recognition for conversation
this.currentCall.startVoiceRecognition((text) => {
const response = nlp.getBestResponse(text);
this.currentCall.speakResponse(response);
// If the response includes transferring, initiate transfer
if (response.includes('transferring') || response.includes('connecting you')) {
this.currentCall.transfer();
}
});
// Log the answered call
this.logInteraction(callerInfo, greeting, 'answered');
},
endCall: function() {
if (!this.isCallActive) return;
this.currentCall.hangup();
this.isCallActive = false;
this.currentCall = null;
// Reset UI
document.querySelector('.fab i').classList.remove('fa-phone-alt');
document.querySelector('.fab i').classList.add('fa-microphone');
document.querySelector('.fab').style.background = 'linear-gradient(135deg, #0a84ff, #5e5ce6)';
},
logInteraction: function(caller, response, type) {
const logEntry = {
date: new Date(),
caller,
response,
type,
duration: type === 'answered' ? Math.floor(Math.random() * 120) + 5 : 0
};
// Add to recent activity
const activityElement = document.createElement('div');
activityElement.className = 'flex items-start animate-fadeIn';
activityElement.innerHTML = `
<div class="w-10 h-10 rounded-full ${type === 'blocked' ? 'bg-purple-100 dark:bg-purple-900' : 'bg-green-100 dark:bg-green-900'} flex items-center justify-center">
<i class="fas fa-phone-alt ${type === 'blocked' ? 'text-purple-600 dark:text-purple-400' : 'text-green-600 dark:text-green-400'}"></i>
</div>
<div class="ml-3 flex-1">
<div class="flex justify-between">
<h3 class="font-medium">${caller}</h3>
<span class="text-xs text-gray-500 dark:text-gray-400">${logEntry.date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
AI ${type} call: "${response.substring(0, 40)}${response.length > 40 ? '...' : ''}"
</p>
</div>
`;
// Add to top of recent activity
const activityContainer = document.querySelector('#homeScreen .space-y-4');
if (activityContainer.firstChild) {
activityContainer.insertBefore(activityElement, activityContainer.firstChild);
} else {
activityContainer.appendChild(activityElement);
}
},
checkActiveCall: function() {
// Simulated check in demo - in real app would check with telephony provider
const activeCall = false;
if (activeCall) {
this.handleIncomingCall({
callerID: 'Incoming call',
answer: () => console.log('Call answered'),
speakResponse: (text) => voiceProcessor.speak(text).play()
});
}
}
};
// Initialize when page loads
document.addEventListener('DOMContentLoaded', () => {
phoneHandler.init();
// Focus input when training screen opens
document.querySelectorAll('[onclick^="showScreen"]').forEach(btn => {
btn.addEventListener('click', function() {
if (this.getAttribute('onclick').includes('trainingScreen')) {
setTimeout(() => {
document.getElementById('trainingInput').focus();
}, 300);
}
});
});
// Simulate incoming call for demo purposes
window.simulateCall = function(callerID) {
phoneHandler.handleIncomingCall({
callerID: callerID || 'Unknown Caller',
answer: function() {
console.log('Call answered');
const audio = voiceProcessor.speak("Hello, this call has been answered by the AI assistant.");
audio.play();
},
speakResponse: function(text) {
const audio = voiceProcessor.speak(text);
audio.play();
},
reject: function() { console.log('Call rejected'); },
hangup: function() { console.log('Call ended'); },
transfer: function() { console.log('Call transferred'); }
});
};
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=jjmandog/ccx" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>