1141 lines
48 KiB
Plaintext
Executable File
1141 lines
48 KiB
Plaintext
Executable File
<?php
|
||
session_start();
|
||
$pdo = new PDO("pgsql:host=localhost;dbname=adx_system", "admin", "admin123");
|
||
$config = [];
|
||
try { $stmt = $pdo->query("SELECT config_key, config_value FROM admin.commonia_config"); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $config[$row['config_key']] = $row['config_value']; } catch (Exception $e) {}
|
||
$defaultProvider = $config['default_provider'] ?? 'cerebras';
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||
<title>HAMID - Assistant WEVADS</title>
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||
<style>
|
||
:root{--bg:#f8f8f8;--sidebar:#fff;--chat:#fff;--text:#1a1a1a;--muted:#6b7280;--border:#e5e7eb;--primary:#06b6d4}
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
body{font-family:-apple-system,sans-serif;background:var(--bg);color:var(--text);height:100vh;overflow:hidden}
|
||
.app{display:flex;height:100vh}
|
||
.sidebar{width:260px;background:var(--sidebar);border-right:1px solid var(--border);display:flex;flex-direction:column}
|
||
.sidebar-header{padding:1rem}
|
||
.new-chat{width:100%;padding:.75rem 1rem;background:linear-gradient(135deg,#06b6d4,#3b82f6);color:#fff;border:none;border-radius:8px;cursor:pointer;display:flex;align-items:center;gap:.5rem}
|
||
.sidebar-section{padding:.5rem 1rem}
|
||
.sidebar-title{font-size:.7rem;font-weight:600;color:var(--muted);text-transform:uppercase;margin-bottom:.5rem}
|
||
.sidebar-item{display:flex;align-items:center;gap:.75rem;padding:.6rem .75rem;border-radius:6px;color:var(--text);text-decoration:none;font-size:.9rem}
|
||
.sidebar-item:hover{background:#f3f4f6}
|
||
.sidebar-footer{margin-top:auto;padding:1rem;border-top:1px solid var(--border)}
|
||
.sidebar-footer a{display:flex;align-items:center;gap:.5rem;padding:.5rem;color:var(--muted);text-decoration:none;font-size:.85rem}
|
||
.main{flex:1;display:flex;flex-direction:column}
|
||
.header{padding:.75rem 1.5rem;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
|
||
.header-left{display:flex;align-items:center;gap:.75rem}
|
||
.logo{width:36px;height:36px;border-radius:8px;background:linear-gradient(135deg,#06b6d4,#3b82f6);display:flex;align-items:center;justify-content:center;font-size:1.2rem}
|
||
.header-right{display:flex;align-items:center;gap:.5rem}
|
||
.help-btn{padding:.5rem .75rem;border:1px solid var(--border);background:#fff;border-radius:8px;cursor:pointer}
|
||
.provider-select{position:relative}
|
||
.provider-btn{padding:.5rem 1rem;border:1px solid var(--border);background:#fff;border-radius:8px;cursor:pointer;display:flex;align-items:center;gap:.5rem}
|
||
.provider-btn .dot{width:8px;height:8px;border-radius:50%;background:#10b981}
|
||
.provider-menu{position:absolute;top:100%;right:0;margin-top:.5rem;background:#fff;border:1px solid var(--border);border-radius:12px;box-shadow:0 10px 40px rgba(0,0,0,.15);min-width:280px;z-index:100;display:none}
|
||
.provider-menu.show{display:block}
|
||
.provider-group{padding:.5rem}
|
||
.provider-group-title{padding:.5rem .75rem;font-size:.7rem;font-weight:600;text-transform:uppercase}
|
||
.provider-group-title.free{color:#10b981}
|
||
.provider-group-title.limited{color:#f59e0b}
|
||
.provider-group-title.paid{color:#ef4444}
|
||
.provider-option{display:flex;align-items:center;gap:.75rem;padding:.6rem .75rem;border-radius:8px;cursor:pointer}
|
||
.provider-option:hover{background:#f3f4f6}
|
||
.provider-option.active{background:#e0f2fe}
|
||
.provider-option .icon{font-size:1.25rem}
|
||
.provider-option .info{flex:1}
|
||
.provider-option .name{font-size:.9rem;font-weight:500}
|
||
.provider-option .desc{font-size:.75rem;color:var(--muted)}
|
||
.messages{flex:1;overflow-y:auto;padding:1.5rem}
|
||
.welcome{text-align:center;padding:3rem 1rem}
|
||
.welcome-icon{width:80px;height:80px;background:linear-gradient(135deg,#06b6d4,#3b82f6);border-radius:20px;display:flex;align-items:center;justify-content:center;margin:0 auto 1.5rem;font-size:2.5rem}
|
||
.welcome h2{margin-bottom:.75rem}
|
||
.welcome p{color:var(--muted);margin-bottom:2rem}
|
||
.quick-actions{display:flex;gap:.75rem;justify-content:center;flex-wrap:wrap}
|
||
.quick-btn{padding:.75rem 1.25rem;border:2px solid var(--border);background:#fff;border-radius:10px;cursor:pointer;display:flex;align-items:center;gap:.5rem}
|
||
.quick-btn:hover{border-color:var(--primary);background:#f0fdfa}
|
||
.msg{display:flex;gap:1rem;margin-bottom:1.5rem;max-width:900px;margin-left:auto;margin-right:auto}
|
||
.msg-user{justify-content:flex-end}
|
||
.msg-user .bubble{background:#f3f4f6;padding:.875rem 1rem;border-radius:16px 16px 4px 16px;max-width:70%}
|
||
.msg-assistant{align-items:flex-start}
|
||
.msg-assistant .avatar{width:32px;height:32px;background:linear-gradient(135deg,#06b6d4,#3b82f6);border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.9rem}
|
||
.msg-assistant .msg-wrap{flex:1;max-width:80%}
|
||
.msg-assistant .msg-content{line-height:1.6}
|
||
.msg-footer{margin-top:.5rem;padding-top:.5rem;border-top:1px solid var(--border);font-size:.7rem;color:var(--muted);display:flex;gap:.4rem;flex-wrap:wrap}
|
||
.msg-footer .tag{padding:.2rem .5rem;border-radius:4px;background:#f3f4f6}
|
||
.msg-footer .tag.prov{background:#dbeafe;color:#1e40af}
|
||
.msg-footer .tag.kb{background:#d1fae5;color:#065f46}
|
||
.typing{display:none;padding:1rem 1.5rem;max-width:900px;margin:0 auto}
|
||
.typing.show{display:flex !important;align-items:center;gap:.75rem}
|
||
.typing-avatar{width:32px;height:32px;background:linear-gradient(135deg,#06b6d4,#3b82f6);border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.9rem}
|
||
.typing-dots{display:flex;gap:4px;padding:.75rem 1rem;background:#f3f4f6;border-radius:16px}
|
||
.typing-dots span{width:8px;height:8px;background:#94a3b8;border-radius:50%;animation:bounce 1.4s infinite}
|
||
.typing-dots span:nth-child(2){animation-delay:.2s}
|
||
.typing-dots span:nth-child(3){animation-delay:.4s}
|
||
@keyframes bounce{0%,80%,100%{transform:translateY(0)}40%{transform:translateY(-6px)}}
|
||
.input-area{padding:1rem 1.5rem 1.5rem;border-top:1px solid var(--border)}
|
||
.input-wrap{display:flex;gap:.5rem;max-width:900px;margin:0 auto;background:#fff;border:1px solid var(--border);border-radius:12px;padding:.5rem;align-items:flex-end}
|
||
.input-wrap:focus-within{border-color:var(--primary)}
|
||
.input-wrap textarea{flex:1;border:none;outline:none;resize:none;font-size:.95rem;padding:.5rem;max-height:120px}
|
||
.input-btn{width:40px;height:40px;background:none;border:none;color:var(--muted);cursor:pointer;border-radius:8px;font-size:1.1rem}
|
||
.input-btn:hover{background:#f3f4f6;color:var(--primary)}
|
||
.send-btn{width:40px;height:40px;background:linear-gradient(135deg,#06b6d4,#3b82f6);color:#fff;border:none;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center}
|
||
#artPanel{position:fixed;right:-500px;top:0;width:500px;height:100vh;background:#fff;border-left:1px solid var(--border);display:flex;flex-direction:column;transition:right .3s;z-index:200}
|
||
#artPanel.open{right:0;box-shadow:-5px 0 30px rgba(0,0,0,.1)}
|
||
.art-header{padding:1rem;border-bottom:1px solid var(--border);display:flex;justify-content:space-between}
|
||
.art-close{background:none;border:none;font-size:1.5rem;cursor:pointer;color:var(--muted)}
|
||
#artContent{flex:1;overflow:auto;background:#f8fafc}
|
||
#artContent iframe{width:100%;height:100%;border:none}
|
||
.art-footer{padding:1rem;border-top:1px solid var(--border)}
|
||
.art-footer .btn{padding:.5rem 1rem;border:1px solid var(--border);background:#fff;border-radius:6px;text-decoration:none;color:var(--text);display:inline-flex;align-items:center;gap:.4rem}
|
||
.modal{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);z-index:300;align-items:center;justify-content:center}
|
||
.modal.show{display:flex}
|
||
.modal-content{background:#fff;border-radius:16px;padding:2rem;max-width:500px;width:90%}
|
||
.modal-content h3{margin-bottom:1rem}
|
||
.modal-content ul{margin:1rem 0;padding-left:1.5rem}
|
||
.modal-content li{margin-bottom:.5rem;color:var(--muted)}
|
||
.modal-content button{margin-top:1rem;padding:.75rem 1.5rem;background:var(--primary);color:#fff;border:none;border-radius:8px;cursor:pointer}
|
||
@media(max-width:768px){.sidebar{display:none}}
|
||
|
||
.msg-tag{display:inline-block;font-size:10px;padding:2px 6px;border-radius:4px;margin-left:4px}
|
||
.msg-tag.kb{background:#10b981;color:white}
|
||
.msg-tag.ia{background:#8b5cf6;color:white}
|
||
|
||
/* Tools Panel */
|
||
.tools-panel {
|
||
background: rgba(15, 23, 42, 0.95);
|
||
border: 1px solid #334155;
|
||
border-radius: 12px;
|
||
padding: 1rem;
|
||
margin: 0.5rem;
|
||
}
|
||
.tools-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 0.5rem;
|
||
}
|
||
.tool-btn {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
padding: 0.75rem 0.5rem;
|
||
background: linear-gradient(135deg, #1e293b, #0f172a);
|
||
border: 1px solid #334155;
|
||
border-radius: 8px;
|
||
color: #e2e8f0;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
font-size: 0.75rem;
|
||
}
|
||
.tool-btn:hover {
|
||
border-color: #06b6d4;
|
||
background: linear-gradient(135deg, #1e3a5f, #0f172a);
|
||
transform: translateY(-2px);
|
||
}
|
||
.tool-btn .icon { font-size: 1.25rem; }
|
||
.tool-btn .label { color: #94a3b8; }
|
||
|
||
.tool-result {
|
||
background: #0f172a;
|
||
border: 1px solid #334155;
|
||
border-radius: 8px;
|
||
padding: 0.75rem;
|
||
margin: 0.5rem 0;
|
||
font-family: monospace;
|
||
font-size: 0.8rem;
|
||
max-height: 200px;
|
||
overflow-y: auto;
|
||
}
|
||
.tool-result pre {
|
||
margin: 0;
|
||
white-space: pre-wrap;
|
||
color: #10b981;
|
||
}
|
||
.tool-header {
|
||
color: #06b6d4;
|
||
font-weight: bold;
|
||
margin-bottom: 0.5rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.quick-actions {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
padding: 0.5rem;
|
||
overflow-x: auto;
|
||
border-top: 1px solid #334155;
|
||
}
|
||
.quick-action {
|
||
padding: 0.4rem 0.8rem;
|
||
background: rgba(6, 182, 212, 0.1);
|
||
border: 1px solid rgba(6, 182, 212, 0.3);
|
||
border-radius: 20px;
|
||
color: #06b6d4;
|
||
font-size: 0.7rem;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
transition: all 0.2s;
|
||
}
|
||
.quick-action:hover {
|
||
background: rgba(6, 182, 212, 0.2);
|
||
border-color: #06b6d4;
|
||
}
|
||
|
||
|
||
/* Thinking Indicator - Style Claude */
|
||
.thinking-container {
|
||
display: none;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
padding: 1rem;
|
||
margin: 0.5rem 0;
|
||
}
|
||
.thinking-container.active {
|
||
display: flex;
|
||
}
|
||
.thinking-spinner {
|
||
width: 24px;
|
||
height: 24px;
|
||
position: relative;
|
||
}
|
||
.thinking-spinner::before {
|
||
content: "✦";
|
||
position: absolute;
|
||
font-size: 24px;
|
||
color: #f97316;
|
||
animation: sparkle 1s ease-in-out infinite;
|
||
}
|
||
@keyframes sparkle {
|
||
0%, 100% { transform: rotate(0deg) scale(1); opacity: 1; }
|
||
50% { transform: rotate(180deg) scale(1.2); opacity: 0.7; }
|
||
}
|
||
.thinking-text {
|
||
color: #94a3b8;
|
||
font-size: 0.9rem;
|
||
font-style: italic;
|
||
}
|
||
.thinking-steps {
|
||
margin-top: 0.5rem;
|
||
padding-left: 2rem;
|
||
color: #64748b;
|
||
font-size: 0.8rem;
|
||
}
|
||
.thinking-step {
|
||
padding: 0.25rem 0;
|
||
opacity: 0;
|
||
animation: fadeIn 0.3s forwards;
|
||
}
|
||
@keyframes fadeIn {
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
/* Artefacts Panel - Style Claude */
|
||
.artifacts-panel {
|
||
width: 320px;
|
||
background: #fafafa;
|
||
border-left: 1px solid #e5e7eb;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
position: fixed;
|
||
right: 0;
|
||
top: 0;
|
||
z-index: 100;
|
||
transform: translateX(100%);
|
||
transition: transform 0.3s ease;
|
||
}
|
||
.artifacts-panel.open {
|
||
transform: translateX(0);
|
||
}
|
||
.artifacts-header {
|
||
padding: 1rem;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
background: white;
|
||
}
|
||
.artifacts-title {
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
.artifacts-close {
|
||
background: none;
|
||
border: none;
|
||
color: #6b7280;
|
||
cursor: pointer;
|
||
font-size: 1.25rem;
|
||
}
|
||
.artifacts-list {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 0.5rem;
|
||
}
|
||
.artifact-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
padding: 0.75rem;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
margin-bottom: 0.5rem;
|
||
background: white;
|
||
border: 1px solid #e5e7eb;
|
||
}
|
||
.artifact-item:hover {
|
||
background: #f3f4f6;
|
||
border-color: #d1d5db;
|
||
}
|
||
.artifact-icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: #f3f4f6;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 1.25rem;
|
||
}
|
||
.artifact-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.artifact-name {
|
||
font-weight: 500;
|
||
color: #1f2937;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.artifact-meta {
|
||
font-size: 0.75rem;
|
||
color: #6b7280;
|
||
}
|
||
.artifact-download {
|
||
color: #6b7280;
|
||
cursor: pointer;
|
||
}
|
||
.artifact-download:hover {
|
||
color: #1f2937;
|
||
}
|
||
|
||
/* Section Contenu du projet */
|
||
.project-section {
|
||
padding: 1rem;
|
||
border-top: 1px solid #e5e7eb;
|
||
}
|
||
.project-title {
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
color: #6b7280;
|
||
text-transform: uppercase;
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
.project-card {
|
||
background: white;
|
||
border: 1px solid #e5e7eb;
|
||
border-radius: 8px;
|
||
padding: 1rem;
|
||
text-align: center;
|
||
}
|
||
.project-name {
|
||
font-weight: 500;
|
||
color: #1f2937;
|
||
margin-bottom: 0.5rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
justify-content: center;
|
||
}
|
||
.project-desc {
|
||
font-size: 0.75rem;
|
||
color: #6b7280;
|
||
}
|
||
|
||
/* Bouton toggle artefacts */
|
||
.artifacts-toggle {
|
||
position: fixed;
|
||
right: 1rem;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
background: white;
|
||
border: 1px solid #e5e7eb;
|
||
border-radius: 8px;
|
||
padding: 0.5rem;
|
||
cursor: pointer;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
z-index: 99;
|
||
transition: all 0.2s;
|
||
}
|
||
.artifacts-toggle:hover {
|
||
background: #f3f4f6;
|
||
}
|
||
.artifacts-toggle.hidden {
|
||
display: none;
|
||
}
|
||
|
||
/* Ajuster le main content quand artefacts ouvert */
|
||
.main-content.with-artifacts {
|
||
margin-right: 320px;
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<div class="sidebar">
|
||
<div class="sidebar-header"><button class="new-chat" onclick="newChat()"><i class="fas fa-plus"></i> Nouvelle conversation</button></div>
|
||
<div class="sidebar-section"><div class="sidebar-title">Outils</div><a href="/ia-tools-test.php" class="sidebar-item"><i class="fas fa-wrench"></i> Test Tools</a><a href="javascript:void(0)" onclick="toggleToolsPanel()" class="sidebar-item" style="background:linear-gradient(135deg,#06b6d4,#3b82f6);color:white"><i class="fas fa-cogs"></i> 🔧 Tools Système</a><a href="/ia-documents.php" class="sidebar-item"><i class="fas fa-folder"></i> Documents</a><a href="/ia-knowledge.php" class="sidebar-item"><i class="fas fa-brain"></i> Knowledge Base</a></div>
|
||
<div class="sidebar-footer"><a href="/ia-index.php"><i class="fas fa-th-large"></i> Index IA</a><a href="/hamid-brain-config.php"><i class="fas fa-cog"></i> Configuration</a></div>
|
||
</div>
|
||
<div class="main">
|
||
<div class="header">
|
||
<div class="header-left"><div class="logo">🧞</div><span style="font-weight:600">HAMID - Assistant WEVADS</span></div>
|
||
<div class="header-right">
|
||
<button class="help-btn" onclick="showHelp()"><i class="fas fa-question-circle"></i> Aide</button>
|
||
<div class="provider-select">
|
||
<button class="provider-btn" onclick="document.getElementById('providerMenu').classList.toggle('show')"><span class="dot"></span><span id="currentProvider"><?= ucfirst($defaultProvider) ?></span><i class="fas fa-chevron-down"></i></button>
|
||
<div class="provider-menu" id="providerMenu">
|
||
<div class="provider-group"><div class="provider-group-title free">✅ Gratuits Illimités</div>
|
||
<div class="provider-option" data-prov="ollama_mini" onclick="selProv('ollama_mini','Ollama Mini')"><span class="icon">⚡</span><div class="info"><div class="name">Ollama Mini</div><div class="desc">Local • Rapide</div></div></div>
|
||
<div class="provider-option" data-prov="ollama" onclick="selProv('ollama','Ollama Mistral')"><span class="icon">🦙</span><div class="info"><div class="name">Ollama Mistral</div><div class="desc">Local • 7B</div></div></div>
|
||
<div class="provider-option" data-prov="deepseek" onclick="selProv('deepseek','DeepSeek')"><span class="icon">🔮</span><div class="info"><div class="name">DeepSeek</div><div class="desc">Cloud • Intelligent</div></div></div>
|
||
<div class="provider-option" data-prov="cerebras" onclick="selProv('cerebras','Cerebras')"><span class="icon">🧠</span><div class="info"><div class="name">Cerebras</div><div class="desc">Ultra rapide</div></div></div>
|
||
<div class="provider-option" data-prov="cerebras" onclick="selProv('cerebras','HAMID Engine')"><span class="icon">⭐</span><div class="info"><div class="name">HAMID Engine</div><div class="desc">Claude-Like • Local</div></div></div>
|
||
<div class="provider-option" data-prov="cerebras" onclick="selProv('cerebras','HAMID Engine')"><span class="icon">⭐</span><div class="info"><div class="name">HAMID Engine</div><div class="desc">Claude-Like • Local</div></div></div>
|
||
<div class="provider-option" data-prov="cerebras" onclick="selProv('cerebras','HAMID Engine')"><span class="icon">⭐</span><div class="info"><div class="name">HAMID Engine</div><div class="desc">Claude-Like • Local</div></div></div>
|
||
<div class="provider-option" data-prov="cerebras" onclick="selProv('cerebras','HAMID Engine')"><span class="icon">⭐</span><div class="info"><div class="name">HAMID Engine</div><div class="desc">Claude-Like • Local</div></div></div>
|
||
<div class="provider-option" data-prov="hyperbolic" onclick="selProv('hyperbolic','Hyperbolic')"><span class="icon">🌀</span><div class="info"><div class="name">Hyperbolic</div><div class="desc">Llama 405B</div></div></div>
|
||
<div class="provider-option" data-prov="mistral" onclick="selProv('mistral','Mistral')"><span class="icon">🇫🇷</span><div class="info"><div class="name">Mistral</div><div class="desc">Français</div></div></div>
|
||
<div class="provider-option" data-prov="cohere" onclick="selProv('cohere','Cohere')"><span class="icon">🔶</span><div class="info"><div class="name">Cohere</div><div class="desc">Command-R+</div></div></div>
|
||
</div>
|
||
<div class="provider-group"><div class="provider-group-title limited">⚠️ Gratuits Limités</div>
|
||
<div class="provider: 'cerebras','Groq')"><span class="icon">🚀</span><div class="info"><div class="name">Groq</div><div class="desc">Ultra rapide</div></div></div>
|
||
<div class="provider-option" data-prov="gemini" onclick="selProv('gemini','Gemini')"><span class="icon">💎</span><div class="info"><div class="name">Gemini</div><div class="desc">Google</div></div></div>
|
||
</div>
|
||
<div class="provider-group"><div class="provider-group-title paid">💰 Payants</div>
|
||
<div class="provider-option" data-prov="claude" onclick="selProv('claude','Claude')"><span class="icon">🧠</span><div class="info"><div class="name">Claude</div><div class="desc">Anthropic</div></div></div>
|
||
<div class="provider-option" data-prov="chatgpt" onclick="selProv('chatgpt','ChatGPT')"><span class="icon">🤖</span><div class="info"><div class="name">ChatGPT</div><div class="desc">OpenAI GPT-4</div></div></div>
|
||
<div class="provider-option" data-prov="copilot" onclick="selProv('copilot','Copilot')"><span class="icon">🐙</span><div class="info"><div class="name">GitHub Copilot</div><div class="desc">Code Assistant</div></div></div>
|
||
</div></div></div></div></div>
|
||
<div class="messages" id="msgs">
|
||
<div class="claude-thinking" id="claudeThinking" style="display:none;background:linear-gradient(135deg,#1a1a2e,#16213e);border:1px solid #4f46e5;border-left:4px solid #f97316;border-radius:12px;padding:16px 20px;margin:16px 0;">
|
||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
|
||
<span style="font-size:24px;animation:spin 2s linear infinite;">✦</span>
|
||
<span style="color:#f97316;font-weight:600;">Réflexion en cours...</span>
|
||
</div>
|
||
<div style="color:#a0aec0;font-size:13px;padding-left:34px;">
|
||
<div>→ Analyse de la demande...</div>
|
||
<div>→ Consultation de la Knowledge Base...</div>
|
||
<div>→ Structuration du contenu...</div>
|
||
<div>→ Génération de la réponse...</div>
|
||
</div>
|
||
</div>
|
||
<style>@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}</style>
|
||
<script>
|
||
function showClaudeThinking(){document.getElementById('claudeThinking').style.display='block';document.getElementById('claudeThinking').scrollIntoView({behavior:'smooth',block:'center'});}
|
||
function hideClaudeThinking(){document.getElementById('claudeThinking').style.display='none';}
|
||
</script>
|
||
<!-- Thinking Indicator -->
|
||
<div class="thinking-container" id="thinkingIndicator">
|
||
<div class="thinking-spinner"></div>
|
||
<div>
|
||
<div class="thinking-text" id="thinkingText">Réflexion en cours...</div>
|
||
<div class="thinking-steps" id="thinkingSteps"></div>
|
||
</div>
|
||
</div>
|
||
<div class="welcome" id="welcome"><div class="welcome-icon">🧞</div><h2>Bonjour, je suis HAMID</h2><p>Assistant IA WEVADS avec génération de documents</p>
|
||
<!-- ========== THINKING BUBBLE - STYLE CLAUDE ========== -->
|
||
<style>
|
||
.claude-thinking {
|
||
display: none;
|
||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||
border: 1px solid #4f46e5;
|
||
border-left: 4px solid #f97316;
|
||
border-radius: 12px;
|
||
padding: 16px 20px;
|
||
margin: 16px 0;
|
||
max-width: 700px;
|
||
}
|
||
.claude-thinking.visible {
|
||
display: block !important;
|
||
animation: thinkFadeIn 0.3s ease;
|
||
}
|
||
@keyframes thinkFadeIn {
|
||
from { opacity: 0; transform: translateY(-10px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.claude-thinking-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.claude-thinking-icon {
|
||
font-size: 24px;
|
||
animation: sparkleRotate 2s linear infinite;
|
||
}
|
||
@keyframes sparkleRotate {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
.claude-thinking-title {
|
||
color: #f97316;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
}
|
||
.claude-thinking-content {
|
||
color: #a0aec0;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
padding-left: 34px;
|
||
}
|
||
.claude-thinking-step {
|
||
padding: 4px 0;
|
||
opacity: 0;
|
||
animation: stepAppear 0.5s ease forwards;
|
||
}
|
||
.claude-thinking-step:nth-child(1) { animation-delay: 0.2s; }
|
||
.claude-thinking-step:nth-child(2) { animation-delay: 0.6s; }
|
||
.claude-thinking-step:nth-child(3) { animation-delay: 1.0s; }
|
||
.claude-thinking-step:nth-child(4) { animation-delay: 1.4s; }
|
||
@keyframes stepAppear {
|
||
to { opacity: 1; }
|
||
}
|
||
.claude-thinking-step::before {
|
||
content: "→ ";
|
||
color: #f97316;
|
||
}
|
||
</style>
|
||
<div class="quick-actions">
|
||
<button class="quick-btn" onclick="sendQuick('Génère un PDF')"><i class="fas fa-file-pdf" style="color:#ef4444"></i> PDF</button>
|
||
<button class="quick-btn" onclick="sendQuick('Génère un document Word')"><i class="fas fa-file-word" style="color:#2563eb"></i> Word</button>
|
||
<button class="quick-btn" onclick="sendQuick('Génère un fichier Excel')"><i class="fas fa-file-excel" style="color:#16a34a"></i> Excel</button>
|
||
<button class="quick-btn" onclick="sendQuick('Génère une présentation')"><i class="fas fa-file-powerpoint" style="color:#ea580c"></i> PPT</button>
|
||
<button class="quick-btn" onclick="sendQuick('Génère un QR code')"><i class="fas fa-qrcode" style="color:#7c3aed"></i> QR</button>
|
||
</div></div></div>
|
||
<div class="typing" id="typing">
|
||
<div class="typing-avatar">🧞</div>
|
||
<div class="typing-dots"><span></span><span></span><span></span></div>
|
||
<span style="color:var(--muted);font-size:.9rem">Réflexion...</span>
|
||
</div>
|
||
<div class="input-area"><div class="input-wrap">
|
||
<button class="input-btn" onclick="document.getElementById('fileInput').click()" title="Fichier"><i class="fas fa-paperclip"></i></button>
|
||
<button class="input-btn" onclick="document.getElementById('imageInput').click()" title="Image"><i class="fas fa-image"></i></button>
|
||
<input type="file" id="fileInput" style="display:none" multiple accept=".pdf,.doc,.docx,.xls,.xlsx,.txt" onchange="handleFiles(this.files)">
|
||
<input type="file" id="imageInput" style="display:none" multiple accept="image/*,video/*" onchange="handleFiles(this.files)">
|
||
<textarea id="input" placeholder="Écrivez votre message..." rows="1" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();send()}"></textarea>
|
||
<button class="send-btn" onclick="send()"><i class="fas fa-arrow-up"></i></button>
|
||
</div></div></div>
|
||
<div id="artPanel"><div class="art-header"><h3><i class="fas fa-file-alt"></i> <span id="artTitle">Document</span></h3><button class="art-close" onclick="closeArt()">×</button></div><div id="artContent"></div><div class="art-footer"><a id="artDL" href="#" class="btn" download><i class="fas fa-download"></i> Télécharger</a></div></div>
|
||
</div>
|
||
<div class="modal" id="helpModal"><div class="modal-content"><h3>🧞 Aide HAMID</h3><p><strong>Génération :</strong></p><ul><li>PDF - "Génère un PDF"</li><li>Word - "Génère un document Word"</li><li>Excel - "Génère un fichier Excel"</li><li>PPT - "Génère une présentation"</li><li>QR - "Génère un QR code"</li></ul><p>📎 Fichiers | 🖼️ Images</p><button onclick="document.getElementById('helpModal').classList.remove('show')">OK</button></div></div>
|
||
<script>
|
||
let prov='<?= $defaultProvider ?>';let hist=[];let files=[];
|
||
document.addEventListener('DOMContentLoaded',()=>{const o=document.querySelector('[data-prov="'+prov+'"]');if(o)o.classList.add('active')});
|
||
function esc(s){const d=document.createElement('div');d.textContent=s;return d.innerHTML}
|
||
function fmt(s){return s.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>').replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank">$1</a>').replace(/\n/g,'<br>')}
|
||
function selProv(p,n){prov=p;document.getElementById('currentProvider').textContent=n;document.querySelectorAll('.provider-option').forEach(e=>e.classList.remove('active'));const o=document.querySelector('[data-prov="'+p+'"]');if(o)o.classList.add('active');document.getElementById('providerMenu').classList.remove('show')}
|
||
function showHelp(){document.getElementById('helpModal').classList.add('show')}
|
||
function newChat(){hist=[];files=[];document.getElementById('msgs').innerHTML='<div class="welcome" id="welcome"><div class="welcome-icon">🧞</div><h2>Bonjour</h2><p>Assistant IA WEVADS</p><div class="quick-actions"><button class="quick-btn" onclick="sendQuick(\'Génère un PDF\')"><i class="fas fa-file-pdf" style="color:#ef4444"></i> PDF</button><button class="quick-btn" onclick="sendQuick(\'Génère un Word\')"><i class="fas fa-file-word" style="color:#2563eb"></i> Word</button><button class="quick-btn" onclick="sendQuick(\'Génère un Excel\')"><i class="fas fa-file-excel" style="color:#16a34a"></i> Excel</button></div></div>';closeArt()}
|
||
function sendQuick(m){document.getElementById('input').value=m;send()}
|
||
function handleFiles(f){for(let x of f)files.push(x)}
|
||
function addMsg(role,txt,meta={}){const w=document.getElementById('welcome');if(w)w.style.display='none';const d=document.createElement('div');d.className='msg msg-'+role;if(role==='user')d.innerHTML='<div class="bubble">'+esc(txt)+'</div>';else{let tags='<span class="tag prov">'+(meta.provider||prov.toUpperCase())+'</span>';if(meta.kb_used)tags+='<span class="tag kb">📚 KB</span>';if(meta.duration)tags+='<span class="tag">'+meta.duration+'ms</span>';d.innerHTML='<div class="avatar">🧞</div><div class="msg-wrap"><div class="msg-content">'+fmt(txt)+'</div><div class="msg-footer">'+tags+'</div></div>'}document.getElementById('msgs').appendChild(d);document.getElementById('msgs').scrollTop=9999}
|
||
async function send(){
|
||
showClaudeThinking(); // showThinking(["Analyse de la demande...", "Consultation de la Knowledge Base...", "Réflexion et structuration...", "Génération de la réponse..."]);
|
||
const inp=document.getElementById('input');const m=inp.value.trim();if(!m)return;inp.value='';
|
||
addMsg('user',m);hist.push({role:'user',content:m});
|
||
updateThinkingText('🧠 Analyse en cours...');
|
||
try{
|
||
const fd=new FormData();fd.append('message',m);fd.append('provider',prov);fd.append('history',JSON.stringify(hist.slice(-6)));files.forEach(f=>fd.append('files[]',f));
|
||
const r=await fetch('/api/widget-api.php',{method:'POST',body:fd});const d=await r.json();
|
||
hideClaudeThinking();
|
||
if(d.success){addMsg('assistant',d.response,{provider:d.provider,duration:d.duration_ms,kb_used:d.kb_used});hist.push({role:'assistant',content:d.response});if(d.artifacts&&d.artifacts.length>0)showArt(d.artifacts[0])}
|
||
else addMsg('assistant','❌ '+(d.error||'Erreur'))
|
||
}catch(e){hideClaudeThinking();addMsg('assistant','❌ '+e.message)}
|
||
files=[]
|
||
}
|
||
function showArt(a){document.getElementById('artTitle').textContent=a.title||'Document';document.getElementById('artDL').href=a.url;const c=document.getElementById('artContent');if(a.type==='pdf')c.innerHTML='<iframe src="'+a.url+'"></iframe>';else if(a.type==='image')c.innerHTML='<div style="display:flex;align-items:center;justify-content:center;height:100%;padding:1rem"><img src="'+a.url+'" style="max-width:100%;max-height:100%"></div>';else c.innerHTML='<div style="text-align:center;padding:3rem"><i class="fas fa-file-alt" style="font-size:4rem;color:#06b6d4"></i><h3 style="margin:1rem 0">'+a.file+'</h3><a href="'+a.url+'" class="btn" download>Télécharger</a></div>';document.getElementById('artPanel').classList.add('open')}
|
||
function closeArt(){document.getElementById('artPanel').classList.remove('open')}
|
||
document.addEventListener('click',e=>{if(!e.target.closest('.provider-select'))document.getElementById('providerMenu').classList.remove('show');if(e.target.classList.contains('modal'))e.target.classList.remove('show')});
|
||
|
||
// === TOOLS FUNCTIONS ===
|
||
function toggleTools() {
|
||
const panel = document.getElementById("toolsPanel");
|
||
panel.style.display = panel.style.display === "none" ? "block" : "none";
|
||
}
|
||
|
||
function sendQuick(text) {
|
||
document.getElementById("messageInput").value = text;
|
||
document.getElementById("messageInput").focus();
|
||
}
|
||
|
||
async function runTool(action, params = {}) {
|
||
const url = new URL("/commonia/hamid-tools.php", window.location.origin);
|
||
url.searchParams.set("action", action);
|
||
for (const [k, v] of Object.entries(params)) {
|
||
url.searchParams.set(k, v);
|
||
}
|
||
|
||
try {
|
||
addMessage("assistant", `🔧 Exécution de ${action}...`);
|
||
const res = await fetch(url);
|
||
const data = await res.json();
|
||
|
||
let resultHTML = `<div class="tool-result"><div class="tool-header">🔧 ${action}</div><pre>${JSON.stringify(data, null, 2)}</pre></div>`;
|
||
addMessage("assistant", resultHTML, true);
|
||
} catch (e) {
|
||
addMessage("assistant", `❌ Erreur: ${e.message}`);
|
||
}
|
||
}
|
||
|
||
function promptTool(action, label, param, defaultVal = "") {
|
||
const value = prompt(label, defaultVal);
|
||
if (value) {
|
||
runTool(action, { [param]: value });
|
||
}
|
||
}
|
||
|
||
function addMessage(role, content, isHTML = false) {
|
||
const container = document.getElementById("messagesContainer") || document.querySelector(".messages-container");
|
||
if (!container) return;
|
||
|
||
const msg = document.createElement("div");
|
||
msg.className = "message " + role;
|
||
if (isHTML) {
|
||
msg.innerHTML = content;
|
||
} else {
|
||
msg.textContent = content;
|
||
}
|
||
container.appendChild(msg);
|
||
container.scrollTop = container.scrollHeight;
|
||
}
|
||
|
||
</script>
|
||
</body></html>
|
||
|
||
<!-- Tools Panel Modal -->
|
||
<div id="toolsPanelModal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.8);z-index:9999;padding:2rem;overflow-y:auto">
|
||
<div style="max-width:800px;margin:0 auto;background:#1e293b;border-radius:16px;padding:1.5rem;border:1px solid #334155">
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem">
|
||
<h2 style="color:#06b6d4;margin:0">🔧 Tools Système HAMID</h2>
|
||
<button onclick="toggleToolsPanel()" style="background:none;border:none;color:#94a3b8;font-size:1.5rem;cursor:pointer">✕</button>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:0.75rem;margin-bottom:1rem">
|
||
<button onclick="runTool('status')" class="tool-btn-modal">📊 Status</button>
|
||
<button onclick="runTool('tables')" class="tool-btn-modal">🗄️ Tables DB</button>
|
||
<button onclick="promptAndRun('list','Chemin:','/opt/wevads/public')" class="tool-btn-modal">📁 Fichiers</button>
|
||
<button onclick="promptAndRun('dns','Domaine:')" class="tool-btn-modal">📧 DNS</button>
|
||
<button onclick="promptAndRun('bash','Commande:')" class="tool-btn-modal">💻 Bash</button>
|
||
<button onclick="promptAndRun('sql','Requête SQL:')" class="tool-btn-modal">🔎 SQL</button>
|
||
<button onclick="promptAndRun('read','Chemin fichier:')" class="tool-btn-modal">📄 Lire</button>
|
||
<button onclick="promptAndRun('calc','Expression:')" class="tool-btn-modal">🧮 Calcul</button>
|
||
</div>
|
||
<div id="toolResultArea" style="background:#0f172a;border-radius:8px;padding:1rem;min-height:200px;max-height:400px;overflow-y:auto">
|
||
<div style="color:#64748b;text-align:center">Cliquez sur un tool pour l'exécuter</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.tool-btn-modal{display:flex;flex-direction:column;align-items:center;gap:0.25rem;padding:0.75rem;background:linear-gradient(135deg,#0f172a,#1e293b);border:1px solid #334155;border-radius:8px;color:#e2e8f0;cursor:pointer;font-size:0.85rem;transition:all 0.2s}
|
||
.tool-btn-modal:hover{border-color:#06b6d4;transform:translateY(-2px);background:linear-gradient(135deg,#1e3a5f,#0f172a)}
|
||
</style>
|
||
|
||
<script>
|
||
function toggleToolsPanel(){
|
||
const p=document.getElementById('toolsPanelModal');
|
||
p.style.display=p.style.display==='none'?'flex':'none';
|
||
}
|
||
|
||
async function runTool(action,params={}){
|
||
const area=document.getElementById('toolResultArea');
|
||
area.innerHTML='<div style="color:#06b6d4">⏳ Exécution de '+action+'...</div>';
|
||
|
||
const url=new URL('/commonia/hamid-tools.php',location.origin);
|
||
url.searchParams.set('action',action);
|
||
for(const[k,v]of Object.entries(params))url.searchParams.set(k,v);
|
||
|
||
try{
|
||
const res=await fetch(url);
|
||
const data=await res.json();
|
||
area.innerHTML='<div style="color:#06b6d4;font-weight:bold;margin-bottom:0.5rem">🔧 '+action+'</div><pre style="color:#10b981;white-space:pre-wrap;margin:0">'+JSON.stringify(data,null,2)+'</pre>';
|
||
}catch(e){
|
||
area.innerHTML='<div style="color:#ef4444">❌ Erreur: '+e.message+'</div>';
|
||
}
|
||
}
|
||
|
||
function promptAndRun(action,label,defaultVal=''){
|
||
const val=prompt(label,defaultVal);
|
||
if(val){
|
||
const paramMap={list:'path',dns:'domain',bash:'cmd',sql:'sql',read:'path',calc:'expr',search:'q',fetch:'url'};
|
||
runTool(action,{[paramMap[action]||'q']:val});
|
||
}
|
||
}
|
||
</script>
|
||
|
||
</div>
|
||
|
||
<!-- Bouton Toggle Artefacts -->
|
||
<button class="artifacts-toggle" id="artifactsToggle" onclick="toggleArtifacts()">
|
||
📎
|
||
</button>
|
||
|
||
<!-- Panneau Artefacts -->
|
||
<div class="artifacts-panel" id="artifactsPanel">
|
||
<div class="artifacts-header">
|
||
<div class="artifacts-title">📎 Artefacts</div>
|
||
<button class="artifacts-close" onclick="toggleArtifacts()">✕</button>
|
||
</div>
|
||
|
||
<div class="artifacts-list" id="artifactsList">
|
||
<!-- Les artefacts seront ajoutés dynamiquement -->
|
||
</div>
|
||
|
||
<div class="project-section">
|
||
<div class="project-title">Contenu du projet</div>
|
||
<div class="project-card">
|
||
<div class="project-name">🧞 HAMID IA System</div>
|
||
<div class="project-desc">Créé par vous</div>
|
||
<div style="margin-top:0.75rem;padding:0.5rem;background:#f9fafb;border-radius:6px">
|
||
<small style="color:#6b7280">Ajoutez des PDF, documents ou autres textes à référencer</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Toggle Artefacts Panel
|
||
function toggleArtifacts() {
|
||
const panel = document.getElementById('artifactsPanel');
|
||
const toggle = document.getElementById('artifactsToggle');
|
||
const main = document.querySelector('.chat-container') || document.querySelector('.main-content');
|
||
|
||
panel.classList.toggle('open');
|
||
toggle.classList.toggle('hidden');
|
||
if (main) main.classList.toggle('with-artifacts');
|
||
}
|
||
|
||
// Thinking Indicator Functions
|
||
function showThinking(steps = []) {
|
||
const container = document.getElementById('thinkingIndicator');
|
||
const stepsDiv = document.getElementById('thinkingSteps');
|
||
|
||
container.classList.add('active');
|
||
container.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
stepsDiv.innerHTML = '';
|
||
|
||
if (steps.length > 0) {
|
||
steps.forEach((step, i) => {
|
||
setTimeout(() => {
|
||
const stepEl = document.createElement('div');
|
||
stepEl.className = 'thinking-step';
|
||
stepEl.textContent = '• ' + step;
|
||
stepEl.style.animationDelay = (i * 0.2) + 's';
|
||
stepsDiv.appendChild(stepEl);
|
||
}, i * 500);
|
||
});
|
||
}
|
||
}
|
||
|
||
function hideThinking() {
|
||
document.getElementById('thinkingIndicator').classList.remove('active');
|
||
}
|
||
|
||
function updateThinkingText(text) {
|
||
document.getElementById('thinkingText').textContent = text;
|
||
}
|
||
|
||
// Add Artifact to Panel
|
||
function addArtifact(artifact) {
|
||
const list = document.getElementById('artifactsList');
|
||
const icons = {
|
||
'pdf': '📄',
|
||
'doc': '📝',
|
||
'docx': '📝',
|
||
'xlsx': '📊',
|
||
'xls': '📊',
|
||
'pptx': '📽️',
|
||
'ppt': '📽️',
|
||
'html': '🌐',
|
||
'php': '💻',
|
||
'js': '⚡',
|
||
'json': '📋',
|
||
'txt': '📃',
|
||
'code': '💻',
|
||
'image': '🖼️',
|
||
'default': '📎'
|
||
};
|
||
|
||
const ext = artifact.type || artifact.name?.split('.').pop() || 'default';
|
||
const icon = icons[ext] || icons['default'];
|
||
|
||
const item = document.createElement('div');
|
||
item.className = 'artifact-item';
|
||
item.innerHTML = `
|
||
<div class="artifact-icon">${icon}</div>
|
||
<div class="artifact-info">
|
||
<div class="artifact-name">${artifact.name || 'Document'}</div>
|
||
<div class="artifact-meta">${artifact.meta || artifact.type || 'Fichier'}</div>
|
||
</div>
|
||
<a href="${artifact.url || '#'}" download class="artifact-download">⬇️</a>
|
||
`;
|
||
|
||
if (artifact.url) {
|
||
item.onclick = () => window.open(artifact.url, '_blank');
|
||
}
|
||
|
||
list.insertBefore(item, list.firstChild);
|
||
|
||
// Ouvrir le panel si fermé
|
||
if (!document.getElementById('artifactsPanel').classList.contains('open')) {
|
||
toggleArtifacts();
|
||
}
|
||
}
|
||
|
||
// Override send function to show thinking
|
||
const originalSend = window.sendMessage;
|
||
if (typeof originalSend === 'function') {
|
||
window.sendMessage = async function(...args) {
|
||
showClaudeThinking(); // showThinking(['Analyse de la requête...', 'Consultation de la Knowledge Base...', 'Génération de la réponse...']);
|
||
try {
|
||
const result = await originalSend.apply(this, args);
|
||
hideClaudeThinking();
|
||
return result;
|
||
} catch(e) {
|
||
hideClaudeThinking();
|
||
throw e;
|
||
}
|
||
};
|
||
}
|
||
|
||
// Exemple: Ajouter quelques artefacts de démonstration
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Charger les documents existants
|
||
fetch('/commonia/hamid-tools.php?action=sql&sql=SELECT id,title,type,url FROM admin.hamid_documents ORDER BY id DESC LIMIT 10')
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success && data.data) {
|
||
data.data.forEach(doc => {
|
||
addArtifact({
|
||
name: doc.title,
|
||
type: doc.type,
|
||
url: doc.url,
|
||
meta: doc.type
|
||
});
|
||
});
|
||
}
|
||
})
|
||
.catch(() => {});
|
||
});
|
||
</script>
|
||
|
||
<!-- Relevant Chats Button & Panel -->
|
||
<button class="relevant-chats-btn" onclick="toggleRelevantPanel()" id="relevantBtn">
|
||
<span>🔍 Relevant chats</span>
|
||
<span class="count" id="relevantCount">0</span>
|
||
</button>
|
||
|
||
<div class="relevant-panel" id="relevantPanel">
|
||
<div class="relevant-header">
|
||
<h3>🔍 Conversations pertinentes</h3>
|
||
<button class="relevant-close" onclick="toggleRelevantPanel()">×</button>
|
||
</div>
|
||
<div class="relevant-list" id="relevantList">
|
||
<div class="relevant-empty">
|
||
<p>💬 Les conversations similaires apparaîtront ici</p>
|
||
<small>Commencez à taper pour rechercher</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Relevant Chats System
|
||
let relevantSearchTimeout = null;
|
||
let lastSearchQuery = '';
|
||
|
||
function toggleRelevantPanel() {
|
||
const panel = document.getElementById('relevantPanel');
|
||
panel.classList.toggle('open');
|
||
}
|
||
|
||
function searchRelevantChats(query) {
|
||
if (query.length < 3 || query === lastSearchQuery) return;
|
||
lastSearchQuery = query;
|
||
|
||
clearTimeout(relevantSearchTimeout);
|
||
relevantSearchTimeout = setTimeout(() => {
|
||
const list = document.getElementById('relevantList');
|
||
list.innerHTML = '<div class="relevant-searching">🔄 Recherche en cours...</div>';
|
||
|
||
fetch('/commonia/hamid-search.php?q=' + encodeURIComponent(query) + '&limit=10')
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success && data.count > 0) {
|
||
document.getElementById('relevantCount').textContent = data.count;
|
||
renderRelevantResults(data.results);
|
||
} else {
|
||
document.getElementById('relevantCount').textContent = '0';
|
||
list.innerHTML = '<div class="relevant-empty"><p>Aucune conversation similaire</p></div>';
|
||
}
|
||
})
|
||
.catch(err => {
|
||
list.innerHTML = '<div class="relevant-empty"><p>❌ Erreur de recherche</p></div>';
|
||
});
|
||
}, 500);
|
||
}
|
||
|
||
function renderRelevantResults(results) {
|
||
const list = document.getElementById('relevantList');
|
||
list.innerHTML = '';
|
||
|
||
results.forEach(item => {
|
||
const div = document.createElement('div');
|
||
div.className = 'relevant-item';
|
||
div.innerHTML = `
|
||
<div class="question">💬 ${escapeHtml(item.question)}</div>
|
||
<div class="response">${escapeHtml(item.response || 'Pas de réponse')}</div>
|
||
<div class="meta">
|
||
<span class="provider">${item.provider || 'IA'}</span>
|
||
<span>${item.date}</span>
|
||
</div>
|
||
`;
|
||
div.onclick = () => useRelevantChat(item);
|
||
list.appendChild(div);
|
||
});
|
||
}
|
||
|
||
function escapeHtml(text) {
|
||
if (!text) return '';
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
function useRelevantChat(item) {
|
||
// Insérer la question dans l'input
|
||
const input = document.getElementById('messageInput') || document.querySelector('textarea');
|
||
if (input) {
|
||
input.value = item.question;
|
||
input.focus();
|
||
}
|
||
toggleRelevantPanel();
|
||
}
|
||
|
||
// Hook sur l'input pour rechercher automatiquement
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const input = document.getElementById('messageInput') || document.querySelector('textarea');
|
||
if (input) {
|
||
input.addEventListener('input', function() {
|
||
const query = this.value.trim();
|
||
if (query.length >= 3) {
|
||
searchRelevantChats(query);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<script>
|
||
// Auto-save conversations to database
|
||
function saveConversation(userMessage, aiResponse, provider) {
|
||
const sessionId = window.hamidSessionId || 'session_' + Date.now();
|
||
window.hamidSessionId = sessionId;
|
||
|
||
// Save user message
|
||
fetch('/commonia/hamid-tools.php?action=sql', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||
body: 'sql=' + encodeURIComponent(`INSERT INTO admin.hamid_conversations (session_id, role, content, provider) VALUES ('${sessionId}', 'user', '${userMessage.replace(/'/g, "''")}', '${provider}')`)
|
||
});
|
||
|
||
// Save AI response
|
||
fetch('/commonia/hamid-tools.php?action=sql', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||
body: 'sql=' + encodeURIComponent(`INSERT INTO admin.hamid_conversations (session_id, role, content, provider) VALUES ('${sessionId}', 'assistant', '${aiResponse.replace(/'/g, "''")}', '${provider}')`)
|
||
});
|
||
}
|
||
|
||
// Override the original send to capture conversations
|
||
(function() {
|
||
const originalFetch = window.fetch;
|
||
window.fetch = function(...args) {
|
||
return originalFetch.apply(this, args).then(response => {
|
||
// Clone response to read it
|
||
const clone = response.clone();
|
||
|
||
// Check if it's a chat API call
|
||
if (args[0] && args[0].includes && args[0].includes('commonia-brain.php')) {
|
||
clone.json().then(data => {
|
||
if (data.success && data.response) {
|
||
const input = document.getElementById('messageInput') || document.querySelector('textarea');
|
||
const userMsg = input ? input.dataset.lastMessage : '';
|
||
const provider: 'cerebras';
|
||
if (userMsg) {
|
||
saveConversation(userMsg, data.response, provider);
|
||
}
|
||
}
|
||
}).catch(() => {});
|
||
}
|
||
return response;
|
||
});
|
||
};
|
||
|
||
// Capture message before send
|
||
const input = document.getElementById('messageInput') || document.querySelector('textarea');
|
||
if (input) {
|
||
const form = input.closest('form') || input.parentElement;
|
||
form?.addEventListener('submit', () => {
|
||
input.dataset.lastMessage = input.value;
|
||
});
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter' && !e.shiftKey) {
|
||
input.dataset.lastMessage = input.value;
|
||
}
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
|
||
<style>
|
||
/* Thinking Bubble - Style Claude */
|
||
.thinking-bubble {
|
||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||
border: 1px solid #333;
|
||
border-radius: 16px;
|
||
padding: 16px 20px;
|
||
margin: 10px 0;
|
||
max-width: 85%;
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
.thinking-bubble .thinking-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin-bottom: 12px;
|
||
color: #8b5cf6;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.thinking-bubble .thinking-header .icon {
|
||
animation: pulse 1.5s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.5; }
|
||
}
|
||
|
||
.thinking-bubble .thinking-content {
|
||
color: #a0a0a0;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
font-style: italic;
|
||
border-left: 3px solid #8b5cf6;
|
||
padding-left: 12px;
|
||
margin-left: 5px;
|
||
}
|
||
|
||
.thinking-bubble .thinking-step {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
margin: 8px 0;
|
||
color: #888;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.thinking-bubble .thinking-step.done {
|
||
color: #10b981;
|
||
}
|
||
|
||
.thinking-bubble .thinking-step.active {
|
||
color: #8b5cf6;
|
||
}
|
||
|
||
.thinking-bubble .step-icon {
|
||
min-width: 20px;
|
||
}
|
||
|
||
.thinking-bubble .thinking-steps {
|
||
margin-top: 12px;
|
||
padding-top: 12px;
|
||
border-top: 1px solid #333;
|
||
}
|
||
|
||
.thinking-collapsed {
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
|
||
.thinking-collapsed:hover {
|
||
background: #1e1e3e;
|
||
}
|
||
|
||
.thinking-toggle {
|
||
color: #666;
|
||
font-size: 12px;
|
||
margin-left: auto;
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
/* OVERRIDE - Thinking Bubble Style Claude */
|
||
.thinking-container {
|
||
display: none;
|
||
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);
|
||
border: 1px solid #4f46e5;
|
||
border-radius: 16px;
|
||
padding: 16px 20px;
|
||
margin: 16px auto;
|
||
max-width: 600px;
|
||
box-shadow: 0 4px 20px rgba(79, 70, 229, 0.3);
|
||
}
|
||
.thinking-container.active {
|
||
display: flex !important;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
animation: fadeInUp 0.3s ease;
|
||
}
|
||
@keyframes fadeInUp {
|
||
from { opacity: 0; transform: translateY(10px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.thinking-spinner::before {
|
||
content: "✦";
|
||
font-size: 28px;
|
||
color: #f97316;
|
||
animation: sparkle 1s ease-in-out infinite;
|
||
}
|
||
.thinking-text {
|
||
color: #e0e7ff;
|
||
font-size: 15px;
|
||
font-weight: 500;
|
||
}
|
||
.thinking-steps {
|
||
margin-top: 8px;
|
||
}
|
||
.thinking-step {
|
||
color: #a5b4fc;
|
||
font-size: 13px;
|
||
padding: 4px 0;
|
||
opacity: 0;
|
||
animation: stepFade 0.5s ease forwards;
|
||
}
|
||
@keyframes stepFade {
|
||
to { opacity: 1; }
|
||
}
|
||
</style>
|