jblast94 commited on
Commit
d78b9be
Β·
verified Β·
1 Parent(s): ca91ffe

chat ui with live voice, text input, screen sharing, backend-supabase, workflow-n8n, voice tts-runpod endpoint,voice tts-runpod endpoint, llm-settings menu for providor,api,model or local hosted

Browse files
Files changed (5) hide show
  1. README.md +7 -4
  2. components/navbar.js +76 -0
  3. index.html +48 -19
  4. script.js +162 -0
  5. style.css +52 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Chatverse Ai Powered Communication Hub
3
- emoji: πŸ¦€
4
- colorFrom: gray
5
  colorTo: gray
 
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: ChatVerse - AI-Powered Communication Hub πŸš€
3
+ colorFrom: purple
 
4
  colorTo: gray
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/navbar.js ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ :host {
7
+ display: block;
8
+ position: fixed;
9
+ top: 0;
10
+ left: 0;
11
+ right: 0;
12
+ z-index: 50;
13
+ background-color: var(--primary);
14
+ color: white;
15
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
16
+ }
17
+
18
+ .container {
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: space-between;
22
+ padding: 0.75rem 1rem;
23
+ max-width: 100%;
24
+ }
25
+
26
+ .logo {
27
+ font-weight: 600;
28
+ font-size: 1.25rem;
29
+ display: flex;
30
+ align-items: center;
31
+ }
32
+
33
+ .logo-icon {
34
+ margin-right: 0.5rem;
35
+ }
36
+
37
+ .actions {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 0.75rem;
41
+ }
42
+
43
+ .action-btn {
44
+ background-color: rgba(255, 255, 255, 0.1);
45
+ border-radius: 0.375rem;
46
+ padding: 0.5rem;
47
+ display: flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ cursor: pointer;
51
+ transition: background-color 0.2s;
52
+ }
53
+
54
+ .action-btn:hover {
55
+ background-color: rgba(255, 255, 255, 0.2);
56
+ }
57
+
58
+ .dark-mode-toggle {
59
+ background-color: transparent;
60
+ border: none;
61
+ color: inherit;
62
+ cursor: pointer;
63
+ }
64
+ </style>
65
+
66
+ <div class="container">
67
+ <div class="logo">
68
+ <i class="logo-icon" data-feather="message-square"></i>
69
+ <span>ChatVerse</span>
70
+ </div>
71
+
72
+ <div class="actions">
73
+ <button class="action-btn" id="screen-share-btn" title="Share Screen">
74
+ <i data-feather="monitor"></i>
75
+ </button>
76
+ <button
index.html CHANGED
@@ -1,19 +1,48 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ChatVerse - AI Communication Hub</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <script src="https://unpkg.com/@supabase/supabase-js@2"></script>
12
+ <script src="components/navbar.js"></script>
13
+ <script src="components/sidebar.js"></script>
14
+ <script src="components/chat-input.js"></script>
15
+ <script src="components/llm-settings.js"></script>
16
+ </head>
17
+ <body class="bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100">
18
+ <custom-navbar></custom-navbar>
19
+
20
+ <div class="flex h-screen pt-16">
21
+ <custom-sidebar></custom-sidebar>
22
+
23
+ <main class="flex-1 flex flex-col">
24
+ <div class="flex-1 overflow-y-auto p-4" id="chat-container">
25
+ <!-- Messages will be loaded here -->
26
+ </div>
27
+
28
+ <div class="border-t border-gray-200 dark:border-gray-700 p-4">
29
+ <custom-chat-input></custom-chat-input>
30
+ </div>
31
+ </main>
32
+
33
+ <div class="w-80 border-l border-gray-200 dark:border-gray-700 p-4 hidden lg:block">
34
+ <custom-llm-settings></custom-llm-settings>
35
+ </div>
36
+ </div>
37
+
38
+ <script src="script.js"></script>
39
+ <script>
40
+ feather.replace();
41
+ // Initialize Supabase
42
+ const supabaseUrl = 'YOUR_SUPABASE_URL';
43
+ const supabaseKey = 'YOUR_SUPABASE_KEY';
44
+ const supabase = supabase.createClient(supabaseUrl, supabaseKey);
45
+ </script>
46
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
47
+ </body>
48
+ </html>
script.js ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ // Initialize UI elements
3
+ initVoiceControls();
4
+ initScreenShare();
5
+ initChat();
6
+ });
7
+
8
+ function initVoiceControls() {
9
+ const voiceBtn = document.getElementById('voice-btn');
10
+ const voiceStatus = document.getElementById('voice-status');
11
+
12
+ let isRecording = false;
13
+ let mediaRecorder;
14
+ let audioChunks = [];
15
+
16
+ voiceBtn.addEventListener('click', async () => {
17
+ if (!isRecording) {
18
+ try {
19
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
20
+ mediaRecorder = new MediaRecorder(stream);
21
+
22
+ mediaRecorder.ondataavailable = (e) => {
23
+ audioChunks.push(e.data);
24
+ };
25
+
26
+ mediaRecorder.onstop = async () => {
27
+ const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
28
+ await processVoiceInput(audioBlob);
29
+ audioChunks = [];
30
+ };
31
+
32
+ mediaRecorder.start();
33
+ isRecording = true;
34
+ voiceStatus.textContent = 'Recording...';
35
+ voiceBtn.classList.add('recording');
36
+ } catch (err) {
37
+ console.error('Error accessing microphone:', err);
38
+ }
39
+ } else {
40
+ mediaRecorder.stop();
41
+ isRecording = false;
42
+ voiceStatus.textContent = '';
43
+ voiceBtn.classList.remove('recording');
44
+ }
45
+ });
46
+ }
47
+
48
+ async function processVoiceInput(audioBlob) {
49
+ // Send to RunPod TTS endpoint
50
+ const formData = new FormData();
51
+ formData.append('audio', audioBlob);
52
+
53
+ try {
54
+ const response = await fetch('YOUR_RUNPOD_ENDPOINT', {
55
+ method: 'POST',
56
+ body: formData
57
+ });
58
+
59
+ const { text } = await response.json();
60
+ await sendMessage(text);
61
+ } catch (err) {
62
+ console.error('Error processing voice input:', err);
63
+ }
64
+ }
65
+
66
+ function initScreenShare() {
67
+ const screenShareBtn = document.getElementById('screen-share-btn');
68
+
69
+ screenShareBtn.addEventListener('click', async () => {
70
+ try {
71
+ const stream = await navigator.mediaDevices.getDisplayMedia({
72
+ video: true,
73
+ audio: true
74
+ });
75
+
76
+ const videoElement = document.createElement('video');
77
+ videoElement.srcObject = stream;
78
+ videoElement.autoplay = true;
79
+
80
+ const screenContainer = document.getElementById('screen-share-container');
81
+ screenContainer.innerHTML = '';
82
+ screenContainer.appendChild(videoElement);
83
+
84
+ stream.getVideoTracks()[0].onended = () => {
85
+ screenContainer.innerHTML = '';
86
+ };
87
+ } catch (err) {
88
+ console.error('Error sharing screen:', err);
89
+ }
90
+ });
91
+ }
92
+
93
+ async function initChat() {
94
+ // Load chat history from Supabase
95
+ const { data: messages, error } = await supabase
96
+ .from('messages')
97
+ .select('*')
98
+ .order('created_at', { ascending: true });
99
+
100
+ if (!error && messages) {
101
+ const chatContainer = document.getElementById('chat-container');
102
+ messages.forEach(msg => {
103
+ const messageElement = createMessageElement(msg);
104
+ chatContainer.appendChild(messageElement);
105
+ });
106
+ }
107
+
108
+ // Subscribe to new messages
109
+ supabase
110
+ .channel('messages')
111
+ .on('postgres_changes', {
112
+ event: 'INSERT',
113
+ schema: 'public',
114
+ table: 'messages'
115
+ }, (payload) => {
116
+ const messageElement = createMessageElement(payload.new);
117
+ document.getElementById('chat-container').appendChild(messageElement);
118
+ })
119
+ .subscribe();
120
+ }
121
+
122
+ function createMessageElement(message) {
123
+ const messageDiv = document.createElement('div');
124
+ messageDiv.className = `message mb-4 p-3 rounded-lg max-w-3/4 ${
125
+ message.is_user ? 'bg-blue-100 dark:bg-blue-900 ml-auto' : 'bg-gray-200 dark:bg-gray-800 mr-auto'
126
+ }`;
127
+
128
+ messageDiv.innerHTML = `
129
+ <div class="flex items-start">
130
+ <div class="flex-shrink-0 h-8 w-8 rounded-full ${
131
+ message.is_user ? 'bg-blue-500' : 'bg-purple-500'
132
+ } flex items-center justify-center text-white">
133
+ ${message.is_user ? '<i data-feather="user"></i>' : '<i data-feather="cpu"></i>'}
134
+ </div>
135
+ <div class="ml-3">
136
+ <p class="text-sm font-medium">${message.is_user ? 'You' : 'AI Assistant'}</p>
137
+ <p class="mt-1 text-sm">${message.content}</p>
138
+ <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">${new Date(message.created_at).toLocaleTimeString()}</p>
139
+ </div>
140
+ </div>
141
+ `;
142
+
143
+ feather.replace({ scope: messageDiv });
144
+ return messageDiv;
145
+ }
146
+
147
+ async function sendMessage(content) {
148
+ const { error } = await supabase
149
+ .from('messages')
150
+ .insert([{ content, is_user: true }]);
151
+
152
+ if (!error) {
153
+ // Trigger n8n workflow for AI response
154
+ await fetch('YOUR_N8N_WORKFLOW_URL', {
155
+ method: 'POST',
156
+ headers: {
157
+ 'Content-Type': 'application/json'
158
+ },
159
+ body: JSON.stringify({ message: content })
160
+ });
161
+ }
162
+ }
style.css CHANGED
@@ -1,28 +1,62 @@
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
2
+
3
+ :root {
4
+ --primary: #6366f1;
5
+ --secondary: #8b5cf6;
6
+ }
7
+
8
  body {
9
+ font-family: 'Inter', sans-serif;
10
+ }
11
+
12
+ /* Custom scrollbar */
13
+ ::-webkit-scrollbar {
14
+ width: 8px;
15
+ }
16
+
17
+ ::-webkit-scrollbar-track {
18
+ background: #f1f1f1;
19
+ }
20
+
21
+ ::-webkit-scrollbar-thumb {
22
+ background: #888;
23
+ border-radius: 4px;
24
  }
25
 
26
+ .dark ::-webkit-scrollbar-track {
27
+ background: #1f2937;
 
28
  }
29
 
30
+ .dark ::-webkit-scrollbar-thumb {
31
+ background: #4b5563;
 
 
 
32
  }
33
 
34
+ /* Animation for new messages */
35
+ @keyframes slideIn {
36
+ from {
37
+ opacity: 0;
38
+ transform: translateY(10px);
39
+ }
40
+ to {
41
+ opacity: 1;
42
+ transform: translateY(0);
43
+ }
44
  }
45
 
46
+ .message {
47
+ animation: slideIn 0.3s ease-out;
48
  }
49
+
50
+ /* Voice recording animation */
51
+ @keyframes pulse {
52
+ 0%, 100% {
53
+ opacity: 1;
54
+ }
55
+ 50% {
56
+ opacity: 0.5;
57
+ }
58
+ }
59
+
60
+ .recording {
61
+ animation: pulse 1.5s infinite;
62
+ }