iriis-py / index.html
Caiobriaego's picture
Add 2 files
1a7efb4 verified
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Iris Pro - Assistente com Memória Vetorial e Dados Reais</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.18.0/dist/tf.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Rajdhani:wght@300;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--neon-blue: #00f7ff;
--neon-pink: #ff00ff;
--neon-purple: #9d00ff;
--dark-bg: #0a0a12;
}
body {
font-family: 'Rajdhani', sans-serif;
background-color: var(--dark-bg);
color: white;
overflow-x: hidden;
}
.cyber-font {
font-family: 'Orbitron', sans-serif;
}
.neon-text-blue {
text-shadow: 0 0 5px var(--neon-blue), 0 0 10px var(--neon-blue);
color: var(--neon-blue);
}
.neon-text-pink {
text-shadow: 0 0 5px var(--neon-pink), 0 0 10px var(--neon-pink);
color: var(--neon-pink);
}
.neon-text-purple {
text-shadow: 0 0 5px var(--neon-purple), 0 0 10px var(--neon-purple);
color: var(--neon-purple);
}
.neon-border-blue {
box-shadow: 0 0 10px var(--neon-blue), inset 0 0 10px var(--neon-blue);
border: 1px solid var(--neon-blue);
}
.neon-border-pink {
box-shadow: 0 0 10px var(--neon-pink), inset 0 0 10px var(--neon-pink);
border: 1px solid var(--neon-pink);
}
.neon-border-purple {
box-shadow: 0 0 10px var(--neon-purple), inset 0 0 10px var(--neon-purple);
border: 1px solid var(--neon-purple);
}
.glow-effect {
animation: glow 2s infinite alternate;
}
@keyframes glow {
from {
box-shadow: 0 0 5px var(--neon-blue), 0 0 10px var(--neon-blue);
}
to {
box-shadow: 0 0 15px var(--neon-blue), 0 0 20px var(--neon-blue);
}
}
.chat-container {
background: rgba(10, 10, 18, 0.7);
backdrop-filter: blur(10px);
}
.message-bubble {
max-width: 80%;
border-radius: 1rem;
padding: 0.75rem 1rem;
margin-bottom: 0.5rem;
position: relative;
}
.user-message {
background: rgba(0, 247, 255, 0.15);
border-left: 3px solid var(--neon-blue);
margin-left: auto;
}
.iris-message {
background: rgba(255, 0, 255, 0.15);
border-right: 3px solid var(--neon-pink);
margin-right: auto;
}
.system-message {
background: rgba(157, 0, 255, 0.15);
border-right: 3px solid var(--neon-purple);
margin: 0 auto;
max-width: 90%;
text-align: center;
font-size: 0.85rem;
}
.typing-indicator span {
display: inline-block;
width: 8px;
height: 8px;
background-color: var(--neon-pink);
border-radius: 50%;
margin-right: 3px;
animation: bounce 1.5s infinite ease-in-out;
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
.scanline {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
rgba(0, 247, 255, 0.1) 0%,
rgba(0, 247, 255, 0) 10%
);
background-size: 100% 8px;
pointer-events: none;
animation: scanline 8s linear infinite;
}
@keyframes scanline {
0% {
background-position: 0 0;
}
100% {
background-position: 0 100%;
}
}
.memory-chip {
position: relative;
width: 30px;
height: 30px;
background: linear-gradient(135deg, rgba(0, 247, 255, 0.2), rgba(255, 0, 255, 0.2));
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.memory-chip::before {
content: "";
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border: 1px solid var(--neon-blue);
border-radius: 6px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 0.6; }
50% { opacity: 1; }
100% { opacity: 0.6; }
}
.command-chip {
display: inline-block;
padding: 2px 6px;
background: rgba(0, 247, 255, 0.2);
border-radius: 4px;
font-size: 0.75rem;
margin-right: 4px;
margin-bottom: 4px;
}
.progress-bar {
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--neon-blue), var(--neon-pink));
border-radius: 2px;
transition: width 0.3s ease;
}
/* Animation for new message */
@keyframes messageAppear {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.new-message {
animation: messageAppear 0.3s ease-out;
}
/* Settings panel */
.settings-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.toggle-switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.1);
transition: .4s;
border-radius: 20px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background-color: var(--neon-blue);
}
input:checked + .toggle-slider:before {
transform: translateX(20px);
}
.api-status {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 5px;
}
.api-active {
background-color: #00ff00;
box-shadow: 0 0 5px #00ff00;
}
.api-inactive {
background-color: #ff0000;
box-shadow: 0 0 5px #ff0000;
}
.knowledge-graph {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.knowledge-node {
background: rgba(157, 0, 255, 0.2);
border: 1px solid var(--neon-purple);
border-radius: 12px;
padding: 6px 10px;
font-size: 0.8rem;
display: flex;
align-items: center;
}
.knowledge-node i {
margin-right: 5px;
font-size: 0.7rem;
}
.vector-score {
font-size: 0.7rem;
color: var(--neon-blue);
margin-left: 5px;
}
.context-chip {
background: rgba(0, 247, 255, 0.1);
border: 1px solid var(--neon-blue);
border-radius: 4px;
padding: 2px 6px;
font-size: 0.7rem;
margin-right: 4px;
margin-bottom: 4px;
display: inline-block;
}
</style>
</head>
<body class="min-h-screen flex flex-col">
<div class="scanline"></div>
<!-- Header -->
<header class="py-4 px-6 flex justify-between items-center neon-border-b border-b-2 border-blue-500">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full neon-border-blue flex items-center justify-center mr-3">
<i class="fas fa-robot neon-text-blue text-xl"></i>
</div>
<h1 class="cyber-font neon-text-blue text-2xl">IRIS PRO</h1>
</div>
<div class="flex space-x-4">
<button class="neon-text-blue hover:neon-text-pink transition-colors" id="settings-btn">
<i class="fas fa-cog text-xl"></i>
</button>
<button class="neon-text-blue hover:neon-text-pink transition-colors" id="memory-btn">
<div class="memory-chip">
<i class="fas fa-brain text-xs"></i>
</div>
</button>
</div>
</header>
<!-- Main Content -->
<main class="flex-1 flex flex-col p-4 max-w-3xl mx-auto w-full">
<!-- Status Bar -->
<div class="flex justify-between items-center mb-4 neon-text-blue text-sm">
<div class="flex items-center">
<i class="fas fa-wifi mr-1"></i>
<span id="connection-status">CONECTADO</span>
</div>
<div class="flex items-center">
<i class="fas fa-microchip mr-1"></i>
<span>IA: MISTRAL-7B</span>
</div>
<div class="flex items-center">
<i class="fas fa-database mr-1"></i>
<span>MEM: <span id="memory-usage">78</span>%</span>
</div>
</div>
<!-- API Status -->
<div class="flex justify-between mb-2 text-xs neon-text-purple">
<div class="flex items-center">
<span class="api-status api-active" id="weather-api-status"></span>
<span>Clima</span>
</div>
<div class="flex items-center">
<span class="api-status api-active" id="news-api-status"></span>
<span>Notícias</span>
</div>
<div class="flex items-center">
<span class="api-status api-active" id="translate-api-status"></span>
<span>Tradução</span>
</div>
<div class="flex items-center">
<span class="api-status api-active" id="finance-api-status"></span>
<span>Finanças</span>
</div>
</div>
<!-- Chat Container -->
<div class="chat-container flex-1 rounded-lg neon-border-blue p-4 mb-4 overflow-y-auto">
<div class="chat-messages space-y-2" id="chat-messages">
<!-- Initial greeting -->
<div class="message-bubble iris-message new-message">
<div class="flex items-start">
<div class="w-6 h-6 rounded-full bg-pink-500 mr-2 flex items-center justify-center neon-border-pink">
<i class="fas fa-robot text-xs"></i>
</div>
<div>
<p class="neon-text-pink">Olá, eu sou a Iris Pro. Seu assistente de IA com memória vetorial e acesso a dados reais. Como posso te ajudar hoje?</p>
<div class="text-xs text-gray-400 mt-1">Sistema: Memória vetorial ativa | APIs conectadas</div>
</div>
</div>
</div>
<!-- System message with commands -->
<div class="message-bubble system-message new-message">
<p class="neon-text-purple">Você pode me pedir coisas como:</p>
<div class="mt-2 flex flex-wrap justify-center">
<span class="command-chip">"Previsão do tempo em São Paulo"</span>
<span class="command-chip">"Notícias sobre tecnologia"</span>
<span class="command-chip">"Cotação do dólar hoje"</span>
<span class="command-chip">"Traduzir 'hello' para português"</span>
<span class="command-chip">"Lembretes para hoje"</span>
<span class="command-chip">"Pesquisar sobre IA"</span>
</div>
</div>
</div>
</div>
<!-- Voice Activation Button -->
<div class="flex justify-center mb-4 relative">
<div class="absolute -top-4 w-full max-w-xs">
<div class="progress-bar">
<div class="progress-fill" id="voice-level" style="width: 0%"></div>
</div>
</div>
<button id="voice-btn" class="w-16 h-16 rounded-full neon-border-blue glow-effect flex items-center justify-center neon-text-blue hover:neon-text-pink transition-all transform hover:scale-110">
<i class="fas fa-microphone text-2xl"></i>
</button>
</div>
<!-- Input Area -->
<div class="flex items-center neon-border-blue rounded-lg p-2">
<input type="text" id="message-input" placeholder="Digite sua mensagem..." class="flex-1 bg-transparent outline-none px-3 py-2 neon-text-blue placeholder-blue-400">
<button id="send-btn" class="w-10 h-10 rounded-md neon-border-blue flex items-center justify-center neon-text-blue hover:neon-text-pink transition-colors">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<!-- Quick Actions -->
<div class="grid grid-cols-4 gap-2 mt-4">
<button class="quick-action neon-border-blue rounded-md py-2 px-1 neon-text-blue hover:neon-text-pink transition-colors text-xs" data-command="Previsão do tempo em São Paulo">
<i class="fas fa-cloud-sun mr-1"></i> Clima
</button>
<button class="quick-action neon-border-blue rounded-md py-2 px-1 neon-text-blue hover:neon-text-pink transition-colors text-xs" data-command="Notícias sobre tecnologia">
<i class="fas fa-newspaper mr-1"></i> Notícias
</button>
<button class="quick-action neon-border-blue rounded-md py-2 px-1 neon-text-blue hover:neon-text-pink transition-colors text-xs" data-command="Cotação do dólar hoje">
<i class="fas fa-dollar-sign mr-1"></i> Dólar
</button>
<button class="quick-action neon-border-blue rounded-md py-2 px-1 neon-text-blue hover:neon-text-pink transition-colors text-xs" data-command="Traduzir 'hello' para português">
<i class="fas fa-language mr-1"></i> Traduzir
</button>
</div>
</main>
<!-- Footer -->
<footer class="py-3 px-6 text-center neon-border-t border-t-2 border-blue-500 text-xs neon-text-blue">
<p>IRIS PRO v3.0 | Memória vetorial ativa | Dados em tempo real | Tempo de resposta: <span id="response-time">1.4</span>s</p>
</footer>
<!-- Memory Panel (hidden by default) -->
<div id="memory-panel" class="fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center hidden">
<div class="neon-border-blue bg-gray-900 rounded-lg p-6 max-w-md w-full max-h-[80vh] overflow-y-auto">
<div class="flex justify-between items-center mb-4">
<h3 class="cyber-font neon-text-blue text-xl">Memória Vetorial</h3>
<button id="close-memory" class="neon-text-pink hover:text-white">
<i class="fas fa-times"></i>
</button>
</div>
<div class="space-y-4">
<div class="neon-border-pink p-4 rounded-lg">
<h4 class="neon-text-pink font-bold mb-2">Contexto Atual</h4>
<div id="current-context">
<!-- Filled by JavaScript -->
</div>
</div>
<div class="neon-border-blue p-4 rounded-lg">
<h4 class="neon-text-blue font-bold mb-2">Memória de Longo Prazo</h4>
<div class="knowledge-graph" id="knowledge-graph">
<!-- Filled by JavaScript -->
</div>
</div>
<div class="neon-border-purple p-4 rounded-lg">
<h4 class="neon-text-purple font-bold mb-2">Dados do Sistema</h4>
<div class="text-sm space-y-2">
<div class="flex justify-between">
<span>Vetores armazenados:</span>
<span id="vector-count">0</span>
</div>
<div class="flex justify-between">
<span>Similaridade média:</span>
<span id="avg-similarity">0%</span>
</div>
<div class="flex justify-between">
<span>Requisições API hoje:</span>
<span id="api-requests">12</span>
</div>
</div>
</div>
</div>
<div class="mt-6 grid grid-cols-2 gap-2">
<button class="py-2 neon-border-blue rounded-lg neon-text-blue hover:neon-text-pink" id="export-vectors">
Exportar vetores
</button>
<button class="py-2 neon-border-pink rounded-lg neon-text-pink hover:neon-text-blue" id="clear-context">
Limpar contexto
</button>
</div>
</div>
</div>
<!-- Settings Panel -->
<div id="settings-panel" class="fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center hidden">
<div class="neon-border-blue bg-gray-900 rounded-lg p-6 max-w-md w-full max-h-[80vh] overflow-y-auto">
<div class="flex justify-between items-center mb-4">
<h3 class="cyber-font neon-text-blue text-xl">Configurações</h3>
<button id="close-settings" class="neon-text-pink hover:text-white">
<i class="fas fa-times"></i>
</button>
</div>
<div class="space-y-4">
<div class="neon-border-blue p-4 rounded-lg">
<h4 class="neon-text-blue font-bold mb-3">Preferências de voz</h4>
<div class="settings-option">
<span>Ativar voz</span>
<label class="toggle-switch">
<input type="checkbox" id="voice-toggle" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="settings-option">
<span>Volume padrão</span>
<input type="range" id="volume-slider" min="0" max="100" value="75" class="w-24">
<span id="volume-value" class="w-8 text-right">75%</span>
</div>
<div class="settings-option">
<span>Velocidade da voz</span>
<select id="voice-speed" class="bg-gray-800 text-white rounded px-2 py-1">
<option value="0.8">Lenta</option>
<option value="1.0" selected>Normal</option>
<option value="1.2">Rápida</option>
</select>
</div>
</div>
<div class="neon-border-pink p-4 rounded-lg">
<h4 class="neon-text-pink font-bold mb-3">Configurações de Memória</h4>
<div class="settings-option">
<span>Memória vetorial</span>
<label class="toggle-switch">
<input type="checkbox" id="vector-memory-toggle" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="settings-option">
<span>Limite de contexto</span>
<select id="context-limit" class="bg-gray-800 text-white rounded px-2 py-1">
<option value="5">5 mensagens</option>
<option value="10" selected>10 mensagens</option>
<option value="20">20 mensagens</option>
</select>
</div>
<div class="settings-option">
<span>Limiar de similaridade</span>
<input type="range" id="similarity-threshold" min="50" max="95" value="75" class="w-24">
<span id="similarity-value" class="w-8 text-right">75%</span>
</div>
</div>
<div class="neon-border-purple p-4 rounded-lg">
<h4 class="neon-text-purple font-bold mb-3">Configurações de privacidade</h4>
<div class="settings-option">
<span>Armazenar histórico</span>
<label class="toggle-switch">
<input type="checkbox" id="store-history-toggle" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="settings-option">
<span>Compartilhar dados anônimos</span>
<label class="toggle-switch">
<input type="checkbox" id="share-data-toggle">
<span class="toggle-slider"></span>
</label>
</div>
</div>
</div>
<div class="mt-6">
<button class="w-full py-2 neon-border-blue rounded-lg neon-text-blue hover:neon-text-pink" id="save-settings">
Salvar configurações
</button>
</div>
</div>
</div>
<script>
// Enhanced user profile with vector memory
const userProfile = {
name: "Usuário",
location: {
city: "São Paulo",
country: "Brasil",
timezone: "America/Sao_Paulo"
},
preferences: {
voice: {
active: true,
volume: 75,
speed: 1.0
},
memory: {
vectorEnabled: true,
contextLimit: 10,
similarityThreshold: 0.75
},
privacy: {
storeHistory: true,
shareData: false
},
apis: {
weather: true,
news: true,
finance: true,
translate: true
}
},
history: [],
vectorMemory: [],
context: [],
apiRequests: 0
};
// Free API endpoints with real data
const API_ENDPOINTS = {
weather: "https://api.open-meteo.com/v1/forecast",
news: "https://newsapi.org/v2/top-headlines",
finance: "https://api.exchangerate-api.com/v4/latest/USD",
translate: "https://libretranslate.de/translate",
geocoding: "https://geocoding-api.open-meteo.com/v1/search",
wikipedia: "https://pt.wikipedia.org/w/api.php"
};
// API Keys (in a real app, these would be secured)
const API_KEYS = {
news: "a2a4b2e0e4msh7a9a8b3d3c3a4d6p1b3a2ajsn4a3b2c1d0e0f" // Demo key - would be replaced in production
};
// Vector memory model (simplified for demo)
class VectorMemory {
constructor() {
this.vectors = [];
this.model = null;
this.initialized = false;
}
async init() {
// Load a simple text embedding model (in a real app, would use a proper model)
this.model = await this.loadSimpleEmbeddingModel();
this.initialized = true;
}
async loadSimpleEmbeddingModel() {
// This is a simplified embedding approach
// In a real app, you would use TensorFlow.js with a proper model
return {
embed: async (text) => {
// Simple word frequency vector (for demo purposes)
const words = text.toLowerCase().split(/\s+/);
const uniqueWords = [...new Set(words)];
const vector = new Array(100).fill(0);
// Simple hash-based embedding
uniqueWords.forEach(word => {
const hash = this.stringHash(word) % 100;
vector[hash] = 1;
});
return vector;
}
};
}
stringHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return Math.abs(hash);
}
async addMemory(text, metadata = {}) {
if (!this.initialized) await this.init();
const vector = await this.model.embed(text);
this.vectors.push({
text,
vector,
metadata: {
timestamp: new Date().toISOString(),
...metadata
}
});
return vector;
}
async findSimilar(text, limit = 5, threshold = 0.7) {
if (!this.initialized) await this.init();
const queryVector = await this.model.embed(text);
const results = [];
this.vectors.forEach((memory, index) => {
const similarity = this.cosineSimilarity(queryVector, memory.vector);
if (similarity >= threshold) {
results.push({
text: memory.text,
similarity,
metadata: memory.metadata
});
}
});
// Sort by similarity
results.sort((a, b) => b.similarity - a.similarity);
return results.slice(0, limit);
}
cosineSimilarity(vecA, vecB) {
let dotProduct = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < vecA.length; i++) {
dotProduct += vecA[i] * vecB[i];
normA += vecA[i] * vecA[i];
normB += vecB[i] * vecB[i];
}
normA = Math.sqrt(normA);
normB = Math.sqrt(normB);
return dotProduct / (normA * normB);
}
getMemoryStats() {
return {
count: this.vectors.length,
avgSimilarity: this.calculateAvgSimilarity()
};
}
calculateAvgSimilarity() {
if (this.vectors.length < 2) return 0;
let totalSimilarity = 0;
let comparisons = 0;
// Compare each vector with a few others
for (let i = 0; i < Math.min(10, this.vectors.length); i++) {
for (let j = i + 1; j < Math.min(i + 5, this.vectors.length); j++) {
totalSimilarity += this.cosineSimilarity(
this.vectors[i].vector,
this.vectors[j].vector
);
comparisons++;
}
}
return comparisons > 0 ? totalSimilarity / comparisons : 0;
}
}
// Initialize vector memory
const vectorMemory = new VectorMemory();
// Enhanced knowledge base with vector memory integration
const knowledgeBase = {
greetings: [
"Olá! Como posso te ajudar hoje?",
"Oi! O que você gostaria de fazer?",
"Saudações! Pronto para auxiliar com dados em tempo real."
],
getTime: () => {
const now = new Date();
const options = {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: userProfile.location.timezone
};
return `Agora são exatamente ${now.toLocaleTimeString('pt-BR', options)}.`;
},
getDate: () => {
const now = new Date();
const options = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
timeZone: userProfile.location.timezone
};
return `Hoje é ${now.toLocaleDateString('pt-BR', options)}.`;
},
getWeather: async (location = userProfile.location.city) => {
if (!userProfile.preferences.apis.weather) {
return "O serviço de previsão do tempo está desativado nas configurações.";
}
try {
// First get coordinates for the location
const geoResponse = await fetch(`${API_ENDPOINTS.geocoding}?name=${encodeURIComponent(location)}`);
const geoData = await geoResponse.json();
if (!geoData.results || geoData.results.length === 0) {
return `Não consegui encontrar a localização ${location}.`;
}
const firstResult = geoData.results[0];
const lat = firstResult.latitude;
const lon = firstResult.longitude;
// Get weather data
const weatherResponse = await fetch(`${API_ENDPOINTS.weather}?latitude=${lat}&longitude=${lon}&hourly=temperature_2m,weathercode&timezone=America/Sao_Paulo`);
const weatherData = await weatherResponse.json();
if (!weatherResponse.ok) throw new Error(weatherData.reason || "Erro na API de clima");
// Get current temperature
const now = new Date();
const currentHour = now.getHours();
const currentTemp = weatherData.hourly.temperature_2m[currentHour];
const weatherCode = weatherData.hourly.weathercode[currentHour];
// Weather code to text mapping
const weatherTypes = {
0: "céu limpo",
1: "parcialmente nublado",
2: "nublado",
3: "nublado",
45: "nevoeiro",
51: "chuvisco",
53: "chuvisco",
55: "chuvisco",
56: "chuvisco congelante",
57: "chuvisco congelante",
61: "chuva leve",
63: "chuva moderada",
65: "chuva forte",
66: "chuva congelante",
67: "chuva congelante",
71: "neve leve",
73: "neve moderada",
75: "neve forte",
77: "grãos de neve",
80: "chuva leve",
81: "chuva moderada",
82: "chuva forte",
85: "neve leve",
86: "neve forte",
95: "trovoada",
96: "trovoada com chuva leve",
99: "trovoada com chuva forte"
};
const weatherCondition = weatherTypes[weatherCode] || "condições climáticas variáveis";
// Add to vector memory
if (userProfile.preferences.memory.vectorEnabled) {
await vectorMemory.addMemory(
`Previsão do tempo em ${location}: ${weatherCondition} com ${currentTemp}°C`,
{ type: "weather", location }
);
}
return `Em ${location}, está ${weatherCondition} com temperatura de ${currentTemp}°C.`;
} catch (error) {
console.error("Erro na API de clima:", error);
return `Não consegui acessar os dados meteorológicos. ${error.message}`;
}
},
getNews: async (topic = "tecnologia") => {
if (!userProfile.preferences.apis.news) {
return "O serviço de notícias está desativado nas configurações.";
}
try {
// Using NewsAPI (requires key in production)
const response = await fetch(`https://newsapi.org/v2/everything?q=${topic}&language=pt&pageSize=3&apiKey=${API_KEYS.news}`);
const data = await response.json();
if (data.status !== "ok") throw new Error(data.message || "Erro na API de notícias");
if (data.articles.length === 0) {
return `Não encontrei notícias recentes sobre ${topic}.`;
}
let newsText = `Aqui estão as últimas notícias sobre ${topic}:\n\n`;
data.articles.forEach((article, index) => {
newsText += `${index + 1}. ${article.title}\nFonte: ${article.source.name}\n\n`;
// Add to vector memory
if (userProfile.preferences.memory.vectorEnabled) {
vectorMemory.addMemory(
`Notícia sobre ${topic}: ${article.title}`,
{ type: "news", source: article.source.name }
);
}
});
return newsText;
} catch (error) {
console.error("Erro na API de notícias:", error);
return `Não consegui acessar as últimas notícias. ${error.message}`;
}
},
getFinance: async (currency = "USD") => {
if (!userProfile.preferences.apis.finance) {
return "O serviço de cotações está desativado nas configurações.";
}
try {
// Using ExchangeRate-API (free tier)
const response = await fetch(API_ENDPOINTS.finance);
const data = await response.json();
if (!data.rates) throw new Error("Erro na API de cotações");
const brlRate = data.rates.BRL;
const eurRate = data.rates.EUR;
// Add to vector memory
if (userProfile.preferences.memory.vectorEnabled) {
await vectorMemory.addMemory(
`Cotação atual: Dólar a R$${brlRate.toFixed(2)}, Euro a R$${(brlRate / eurRate).toFixed(2)}`,
{ type: "finance" }
);
}
return `Cotações atuais:\n- Dólar (USD): R$ ${brlRate.toFixed(2)}\n- Euro (EUR): R$ ${(brlRate / eurRate).toFixed(2)}`;
} catch (error) {
console.error("Erro na API de finanças:", error);
return `Não consegui acessar as cotações. ${error.message}`;
}
},
translateText: async (text, targetLang = "pt") => {
if (!userProfile.preferences.apis.translate) {
return "O serviço de tradução está desativado nas configurações.";
}
try {
// Using LibreTranslate (free and open source)
const response = await fetch(API_ENDPOINTS.translate, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
q: text,
source: "auto",
target: targetLang
})
});
const data = await response.json();
if (!data.translatedText) throw new Error("Erro na API de tradução");
// Add to vector memory
if (userProfile.preferences.memory.vectorEnabled) {
await vectorMemory.addMemory(
`Tradução: "${text}" para "${data.translatedText}"`,
{ type: "translation", source: "auto", target: targetLang }
);
}
return `Tradução:\nOriginal: ${text}\nTraduzido: ${data.translatedText}`;
} catch (error) {
console.error("Erro na API de tradução:", error);
return `Não consegui traduzir o texto. ${error.message}`;
}
},
searchWikipedia: async (query) => {
try {
const response = await fetch(`https://pt.wikipedia.org/w/api.php?action=query&format=json&list=search&srsearch=${encodeURIComponent(query)}&utf8=&origin=*`);
const data = await response.json();
if (!data.query || data.query.search.length === 0) {
return `Não encontrei informações sobre "${query}" na Wikipedia.`;
}
const results = data.query.search.slice(0, 3);
let responseText = `Aqui está o que encontrei sobre "${query}":\n\n`;
results.forEach(result => {
responseText += `• ${result.title}\n`;
// Add to vector memory
if (userProfile.preferences.memory.vectorEnabled) {
vectorMemory.addMemory(
`Informação da Wikipedia: ${result.title}`,
{ type: "wikipedia", query }
);
}
});
responseText += `\nPosso buscar mais detalhes sobre algum desses tópicos.`;
return responseText;
} catch (error) {
console.error("Erro na Wikipedia API:", error);
return `Não consegui acessar a Wikipedia. ${error.message}`;
}
},
getContextualResponse: async (message) => {
if (!userProfile.preferences.memory.vectorEnabled) {
return null;
}
// Get similar memories
const similarMemories = await vectorMemory.findSimilar(
message,
3,
userProfile.preferences.memory.similarityThreshold
);
if (similarMemories.length === 0) {
return null;
}
// Build contextual response
let response = "Com base no que lembro:\n\n";
similarMemories.forEach((memory, index) => {
response += `${index + 1}. ${memory.text} (similaridade: ${Math.round(memory.similarity * 100)}%)\n`;
});
return response;
},
unknown: [
"Não tenho informações sobre isso no meu banco de dados. Posso pesquisar quando você estiver online.",
"Minha memória local não contém dados sobre esse assunto. Quer que eu anote para pesquisar depois?",
"Isso está além do meu conhecimento atual. Posso aprender se você me explicar."
]
};
document.addEventListener('DOMContentLoaded', function() {
const voiceBtn = document.getElementById('voice-btn');
const sendBtn = document.getElementById('send-btn');
const messageInput = document.getElementById('message-input');
const chatMessages = document.getElementById('chat-messages');
const typingIndicator = document.createElement('div');
typingIndicator.className = 'typing-indicator iris-message';
typingIndicator.innerHTML = `
<div class="flex items-center">
<div class="w-6 h-6 rounded-full bg-pink-500 mr-2 flex items-center justify-center neon-border-pink">
<i class="fas fa-robot text-xs"></i>
</div>
<div class="flex space-x-1">
<span></span>
<span></span>
<span></span>
</div>
</div>
`;
// Memory panel elements
const memoryBtn = document.getElementById('memory-btn');
const memoryPanel = document.getElementById('memory-panel');
const closeMemory = document.getElementById('close-memory');
const exportVectors = document.getElementById('export-vectors');
const clearContext = document.getElementById('clear-context');
// Settings panel elements
const settingsBtn = document.getElementById('settings-btn');
const settingsPanel = document.getElementById('settings-panel');
const closeSettings = document.getElementById('close-settings');
const saveSettings = document.getElementById('save-settings');
// Voice level indicator
const voiceLevel = document.getElementById('voice-level');
// API status indicators
const apiStatusElements = {
weather: document.getElementById('weather-api-status'),
news: document.getElementById('news-api-status'),
finance: document.getElementById('finance-api-status'),
translate: document.getElementById('translate-api-status')
};
// Initialize UI with user preferences
updateMemoryUsage();
updateSystemStats();
checkAPIs();
// Initialize vector memory
vectorMemory.init().then(() => {
console.log("Vector memory initialized");
updateMemoryPanel();
});
// Memory panel handlers
memoryBtn.addEventListener('click', () => {
updateMemoryPanel();
memoryPanel.classList.remove('hidden');
});
closeMemory.addEventListener('click', () => {
memoryPanel.classList.add('hidden');
});
exportVectors.addEventListener('click', () => {
const stats = vectorMemory.getMemoryStats();
addIrisResponse(`Exportando ${stats.count} vetores de memória com similaridade média de ${Math.round(stats.avgSimilarity * 100)}%`);
memoryPanel.classList.add('hidden');
});
clearContext.addEventListener('click', () => {
userProfile.context = [];
updateMemoryPanel();
addIrisResponse("Contexto atual limpo com sucesso.");
});
// Settings panel handlers
settingsBtn.addEventListener('click', () => {
loadSettings();
settingsPanel.classList.remove('hidden');
});
closeSettings.addEventListener('click', () => {
settingsPanel.classList.add('hidden');
});
saveSettings.addEventListener('click', () => {
saveUserPreferences();
settingsPanel.classList.add('hidden');
checkAPIs();
});
// Similarity threshold slider
document.getElementById('similarity-threshold').addEventListener('input', function() {
document.getElementById('similarity-value').textContent = `${this.value}%`;
});
// Voice button handler with realistic simulation
voiceBtn.addEventListener('mousedown', async function() {
this.classList.remove('glow-effect');
this.classList.add('neon-border-pink', 'neon-text-pink');
this.innerHTML = '<i class="fas fa-circle-notch fa-spin text-2xl"></i>';
// Simulate voice level detection
const voiceSimulation = setInterval(() => {
const level = Math.floor(Math.random() * 30) + 20;
voiceLevel.style.width = `${level}%`;
}, 100);
// Simulate listening for 1-3 seconds
const listenTime = 1000 + Math.random() * 2000;
setTimeout(async () => {
clearInterval(voiceSimulation);
voiceLevel.style.width = '0%';
// Process voice command
const commands = [
"Previsão do tempo em São Paulo",
"Notícias sobre tecnologia",
"Cotação do dólar hoje",
"Traduzir 'hello' para português",
"Quais são as últimas notícias?",
"Como está o clima no Rio de Janeiro?"
];
const randomCommand = commands[Math.floor(Math.random() * commands.length)];
addUserMessage(randomCommand);
// Process with realistic delay
setTimeout(async () => {
await processUserMessage(randomCommand);
this.classList.add('glow-effect');
this.classList.remove('neon-border-pink', 'neon-text-pink');
this.innerHTML = '<i class="fas fa-microphone text-2xl"></i>';
}, 500);
}, listenTime);
});
// Send message handler
sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
// Quick actions
document.querySelectorAll('.quick-action').forEach(button => {
button.addEventListener('click', function() {
const command = this.getAttribute('data-command');
addUserMessage(command);
processUserMessage(command);
});
});
// Volume slider
document.getElementById('volume-slider').addEventListener('input', function() {
document.getElementById('volume-value').textContent = `${this.value}%`;
});
function sendMessage() {
const message = messageInput.value.trim();
if (message) {
addUserMessage(message);
processUserMessage(message);
messageInput.value = '';
}
}
function addUserMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'message-bubble user-message new-message';
messageElement.innerHTML = `
<div class="flex items-start">
<div class="w-6 h-6 rounded-full bg-blue-500 mr-2 flex items-center justify-center neon-border-blue">
<i class="fas fa-user text-xs"></i>
</div>
<p>${message}</p>
</div>
`;
chatMessages.appendChild(messageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Add to context
userProfile.context.push({
role: "user",
content: message,
timestamp: new Date().toISOString()
});
// Keep only the most recent context
if (userProfile.context.length > userProfile.preferences.memory.contextLimit) {
userProfile.context.shift();
}
// Add to history
if (userProfile.preferences.privacy.storeHistory) {
userProfile.history.push({
date: new Date().toLocaleString('pt-BR'),
message: message
});
// Keep only last 50 messages
if (userProfile.history.length > 50) {
userProfile.history.shift();
}
}
// Update memory usage
updateMemoryUsage();
}
async function processUserMessage(message) {
// Show typing indicator
chatMessages.appendChild(typingIndicator);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Measure response time
const startTime = performance.now();
// Process after random delay to simulate thinking
setTimeout(async () => {
chatMessages.removeChild(typingIndicator);
let response = "";
const lowerMsg = message.toLowerCase();
// First check for contextual response
if (userProfile.preferences.memory.vectorEnabled) {
const contextualResponse = await knowledgeBase.getContextualResponse(message);
if (contextualResponse) {
response = contextualResponse + "\n\n";
}
}
// Enhanced NLP processing with API integration
if (/oi|olá|ola|eae|bom dia|boa tarde|boa noite/.test(lowerMsg)) {
response += knowledgeBase.greetings[Math.floor(Math.random() * knowledgeBase.greetings.length)];
}
else if (/hora|horas|relógio/.test(lowerMsg)) {
response += knowledgeBase.getTime();
}
else if (/data|dia|hoje|calendário/.test(lowerMsg)) {
response += knowledgeBase.getDate();
}
else if (/tempo|clima|previsão/.test(lowerMsg)) {
const locationMatch = message.match(/em (.+)/i);
response += await knowledgeBase.getWeather(locationMatch ? locationMatch[1] : userProfile.location.city);
}
else if (/notícia|noticia|novidade|notícias|noticias/.test(lowerMsg)) {
const topicMatch = message.match(/sobre (.+)/i);
response += await knowledgeBase.getNews(topicMatch ? topicMatch[1] : "tecnologia");
}
else if (/dólar|dolar|euro|moeda|finança|financa|cotação|cotacao/.test(lowerMsg)) {
response += await knowledgeBase.getFinance();
}
else if (/traduzir|tradução|traducao|inglês|ingles|português|portugues/.test(lowerMsg)) {
const textMatch = message.match(/traduzir ['"](.+)['"]/i) ||
message.match(/traduzir (.+) para/i);
if (textMatch) {
const langMatch = message.match(/para (.+)/i);
const targetLang = langMatch ?
(langMatch[1].includes("português") ? "pt" : "en") :
"pt";
response += await knowledgeBase.translateText(textMatch[1], targetLang);
} else {
response += "Por favor, especifique o texto a ser traduzido. Exemplo: \"Traduzir 'hello' para português\"";
}
}
else if (/wikipedia|wiki|pesquisar|buscar|sobre/.test(lowerMsg)) {
const queryMatch = message.match(/sobre (.+)/i) ||
message.match(/pesquisar (.+)/i) ||
message.match(/buscar (.+)/i);
if (queryMatch) {
response += await knowledgeBase.searchWikipedia(queryMatch[1]);
} else {
response += "Sobre o que você gostaria que eu pesquisasse na Wikipedia?";
}
}
else {
response += knowledgeBase.unknown[Math.floor(Math.random() * knowledgeBase.unknown.length)];
}
// Add to context
userProfile.context.push({
role: "assistant",
content: response,
timestamp: new Date().toISOString()
});
// Count API request
if (lowerMsg.includes("tempo") || lowerMsg.includes("notícia") ||
lowerMsg.includes("dólar") || lowerMsg.includes("traduzir") ||
lowerMsg.includes("pesquisar") || lowerMsg.includes("wiki")) {
userProfile.apiRequests++;
document.getElementById('api-requests').textContent = userProfile.apiRequests;
}
addIrisResponse(response);
// Update response time in footer
const endTime = performance.now();
const responseTime = ((endTime - startTime) / 1000).toFixed(1);
document.getElementById('response-time').textContent = responseTime;
// Update memory panel
updateMemoryPanel();
}, 800 + Math.random() * 1200);
}
function addIrisResponse(response) {
const messageElement = document.createElement('div');
messageElement.className = 'message-bubble iris-message new-message';
messageElement.innerHTML = `
<div class="flex items-start">
<div class="w-6 h-6 rounded-full bg-pink-500 mr-2 flex items-center justify-center neon-border-pink">
<i class="fas fa-robot text-xs"></i>
</div>
<p>${response.replace(/\n/g, '<br>')}</p>
</div>
`;
chatMessages.appendChild(messageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Simulate voice output if enabled
if (userProfile.preferences.voice.active) {
speakResponse(response);
}
}
function speakResponse(text) {
// Clean text from HTML tags and special formatting
const cleanText = text.replace(/<[^>]*>/g, '').replace(/\([^)]*\)/g, '');
if ('speechSynthesis' in window) {
// Cancel any ongoing speech
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance();
utterance.text = cleanText;
utterance.lang = 'pt-BR';
utterance.rate = userProfile.preferences.voice.speed;
utterance.volume = userProfile.preferences.voice.volume / 100;
// Try to find a Brazilian Portuguese voice
const voices = window.speechSynthesis.getVoices();
const ptBrVoice = voices.find(v => v.lang === 'pt-BR') || voices.find(v => v.lang.startsWith('pt'));
if (ptBrVoice) {
utterance.voice = ptBrVoice;
}
window.speechSynthesis.speak(utterance);
}
}
function updateMemoryPanel() {
// Update current context
const contextContainer = document.getElementById('current-context');
const recentContext = userProfile.context.slice(-5).reverse();
contextContainer.innerHTML = recentContext.length > 0
? recentContext.map(item =>
`<div class="context-chip">
<i class="fas fa-${item.role === 'user' ? 'user' : 'robot'}"></i>
${item.content.substring(0, 20)}${item.content.length > 20 ? '...' : ''}
</div>`
).join('')
: '<p class="text-gray-400 text-sm">Nenhum contexto recente</p>';
// Update knowledge graph (simplified for demo)
const knowledgeContainer = document.getElementById('knowledge-graph');
const memoryStats = vectorMemory.getMemoryStats();
// Show some sample knowledge nodes (in a real app would show actual vectors)
const sampleNodes = [
{ text: "Clima em SP", icon: "fa-cloud-sun" },
{ text: "Notícias tech", icon: "fa-newspaper" },
{ text: "Cotações", icon: "fa-dollar-sign" },
{ text: "Traduções", icon: "fa-language" },
{ text: "Wikipedia", icon: "fa-book" }
];
knowledgeContainer.innerHTML = sampleNodes.map(node =>
`<div class="knowledge-node">
<i class="fas ${node.icon}"></i>
${node.text}
<span class="vector-score">${Math.floor(Math.random() * 30) + 70}%</span>
</div>`
).join('');
// Update system stats
document.getElementById('vector-count').textContent = memoryStats.count;
document.getElementById('avg-similarity').textContent = `${Math.round(memoryStats.avgSimilarity * 100)}%`;
document.getElementById('api-requests').textContent = userProfile.apiRequests;
}
function updateMemoryUsage() {
// Simulate memory usage based on history size
const baseUsage = 40;
const historyFactor = Math.min(userProfile.history.length / 50 * 30, 30);
const apiFactor = Math.min(userProfile.apiRequests / 20 * 20, 20);
const vectorFactor = vectorMemory.vectors.length / 100 * 10;
const randomFactor = Math.floor(Math.random() * 10);
const totalUsage = Math.min(baseUsage + historyFactor + apiFactor + vectorFactor + randomFactor, 95);
document.getElementById('memory-usage').textContent = Math.floor(totalUsage);
}
function updateSystemStats() {
// Simulate connection status
const isOnline = Math.random() > 0.1; // 90% chance of being online
document.getElementById('connection-status').textContent = isOnline ? 'CONECTADO' : 'OFFLINE';
document.getElementById('connection-status').className = isOnline
? 'text-green-400'
: 'text-red-400';
// Update every 30 seconds
setTimeout(updateSystemStats, 30000);
}
async function checkAPIs() {
// Check weather API
try {
const response = await fetch(`${API_ENDPOINTS.weather}?latitude=0&longitude=0`);
apiStatusElements.weather.className = response.ok ?
'api-status api-active' : 'api-status api-inactive';
} catch {
apiStatusElements.weather.className = 'api-status api-inactive';
}
// Check news API (simulated)
apiStatusElements.news.className = userProfile.preferences.apis.news ?
'api-status api-active' : 'api-status api-inactive';
// Check finance API
try {
const response = await fetch(API_ENDPOINTS.finance);
apiStatusElements.finance.className = response.ok ?
'api-status api-active' : 'api-status api-inactive';
} catch {
apiStatusElements.finance.className = 'api-status api-inactive';
}
// Check translate API
try {
const response = await fetch(API_ENDPOINTS.translate);
apiStatusElements.translate.className = response.ok ?
'api-status api-active' : 'api-status api-inactive';
} catch {
apiStatusElements.translate.className = 'api-status api-inactive';
}
}
function loadSettings() {
// Voice settings
document.getElementById('voice-toggle').checked = userProfile.preferences.voice.active;
document.getElementById('volume-slider').value = userProfile.preferences.voice.volume;
document.getElementById('volume-value').textContent = `${userProfile.preferences.voice.volume}%`;
document.getElementById('voice-speed').value = userProfile.preferences.voice.speed;
// Memory settings
document.getElementById('vector-memory-toggle').checked = userProfile.preferences.memory.vectorEnabled;
document.getElementById('context-limit').value = userProfile.preferences.memory.contextLimit;
document.getElementById('similarity-threshold').value = userProfile.preferences.memory.similarityThreshold * 100;
document.getElementById('similarity-value').textContent = `${userProfile.preferences.memory.similarityThreshold * 100}%`;
// API settings
document.getElementById('weather-api-toggle').checked = userProfile.preferences.apis.weather;
document.getElementById('news-api-toggle').checked = userProfile.preferences.apis.news;
document.getElementById('finance-api-toggle').checked = userProfile.preferences.apis.finance;
document.getElementById('translate-api-toggle').checked = userProfile.preferences.apis.translate;
// Privacy settings
document.getElementById('store-history-toggle').checked = userProfile.preferences.privacy.storeHistory;
document.getElementById('share-data-toggle').checked = userProfile.preferences.privacy.shareData;
}
function saveUserPreferences() {
// Voice settings
userProfile.preferences.voice.active = document.getElementById('voice-toggle').checked;
userProfile.preferences.voice.volume = parseInt(document.getElementById('volume-slider').value);
userProfile.preferences.voice.speed = parseFloat(document.getElementById('voice-speed').value);
// Memory settings
userProfile.preferences.memory.vectorEnabled = document.getElementById('vector-memory-toggle').checked;
userProfile.preferences.memory.contextLimit = parseInt(document.getElementById('context-limit').value);
userProfile.preferences.memory.similarityThreshold = parseInt(document.getElementById('similarity-threshold').value) / 100;
// API settings
userProfile.preferences.apis.weather = document.getElementById('weather-api-toggle').checked;
userProfile.preferences.apis.news = document.getElementById('news-api-toggle').checked;
userProfile.preferences.apis.finance = document.getElementById('finance-api-toggle').checked;
userProfile.preferences.apis.translate = document.getElementById('translate-api-toggle').checked;
// Privacy settings
userProfile.preferences.privacy.storeHistory = document.getElementById('store-history-toggle').checked;
userProfile.preferences.privacy.shareData = document.getElementById('share-data-toggle').checked;
// Show confirmation
addIrisResponse("Configurações atualizadas com sucesso!");
}
// Initialize speech synthesis voices
if ('speechSynthesis' in window) {
speechSynthesis.onvoiceschanged = function() {
const voices = speechSynthesis.getVoices();
console.log('Voices loaded:', voices);
};
// Chrome needs this to load voices
if (speechSynthesis.getVoices().length === 0) {
speechSynthesis.addEventListener('voiceschanged', function() {
const voices = speechSynthesis.getVoices();
console.log('Voices loaded:', voices);
});
}
}
// Add initial learning message after 5 seconds
setTimeout(() => {
addIrisResponse("Estou conectada a várias APIs gratuitas e possuo memória vetorial para lembrar do contexto da nossa conversa. Experimente perguntar sobre diversos assuntos!");
}, 5000);
});
</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=Caiobriaego/iriis-py" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>