Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>PitchPerfect by Elevatics.ai</title> | |
| <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"> | |
| <style> | |
| :root { | |
| --primary-color: #007bff; | |
| --secondary-color: #6c757d; | |
| --background-color: #ffffff; | |
| --text-color: #333333; | |
| --message-bg-user: #e9ecef; | |
| --message-bg-bot: #f8f9fa; | |
| --input-bg: #ffffff; | |
| --border-color: #dee2e6; | |
| --scrollbar-thumb: #adb5bd; | |
| --scrollbar-track: #f1f3f5; | |
| } | |
| body.dark-mode { | |
| --primary-color: #4da3ff; | |
| --secondary-color: #a1a8ae; | |
| --background-color: #1a1a1a; | |
| --text-color: #ffffff; | |
| --message-bg-user: #2c2c2c; | |
| --message-bg-bot: #383838; | |
| --input-bg: #2c2c2c; | |
| --border-color: #4a4a4a; | |
| --scrollbar-thumb: #4a4a4a; | |
| --scrollbar-track: #2c2c2c; | |
| } | |
| body { | |
| background-color: var(--background-color); | |
| color: var(--text-color); | |
| transition: background-color 0.3s, color 0.3s; | |
| } | |
| .title { | |
| font-weight: 700; | |
| font-size: 60px ; | |
| background: var(--primary-color); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| text-align: center; | |
| margin: 0; | |
| padding: 2rem 0 0.5rem; | |
| } | |
| .subtitle { | |
| font-size: 24px; | |
| color: var(--subtitle-color); | |
| text-align: center; | |
| } | |
| html, body, #app { | |
| height: 100%; | |
| } | |
| .chat-outer-container { | |
| max-width: 800px; | |
| height: 100vh; | |
| margin: 0 auto; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .chat-container { | |
| flex-grow: 1; | |
| overflow-y: auto; | |
| padding: 1rem; | |
| scrollbar-width: thin; | |
| scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); | |
| } | |
| .chat-container::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .chat-container::-webkit-scrollbar-track { | |
| background: var(--scrollbar-track); | |
| } | |
| .chat-container::-webkit-scrollbar-thumb { | |
| background-color: var(--scrollbar-thumb); | |
| border-radius: 4px; | |
| border: 2px solid var(--scrollbar-track); | |
| } | |
| .message { | |
| padding: 0.75rem; | |
| margin-bottom: 1rem; | |
| border-radius: 15px; | |
| } | |
| .user-message { | |
| background-color: var(--message-bg-user); | |
| border-radius: 15px 15px 0 15px; | |
| } | |
| .bot-message { | |
| background-color: var(--message-bg-bot); | |
| border-radius: 15px 15px 15px 0; | |
| } | |
| .loading-spinner { | |
| width: 3rem; | |
| height: 3rem; | |
| color: var(--primary-color); | |
| } | |
| .presentation-iframe { | |
| width: 100%; | |
| height: 60vh; | |
| border: none; | |
| } | |
| .input-container { | |
| padding: 1rem; | |
| background-color: var(--input-bg); | |
| border-top: 1px solid var(--border-color); | |
| position: relative; | |
| } | |
| .reset-button { | |
| position: absolute; | |
| top: -40px; | |
| right: 10px; | |
| background-color: var(--secondary-color); | |
| color: var(--background-color); | |
| border: none; | |
| border-radius: 50%; | |
| width: 36px; | |
| height: 36px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: background-color 0.3s; | |
| } | |
| .reset-button:hover { | |
| background-color: var(--primary-color); | |
| } | |
| .mode-toggle { | |
| position: fixed; | |
| top: 10px; | |
| right: 10px; | |
| background: none; | |
| border: none; | |
| color: var(--text-color); | |
| font-size: 1.5rem; | |
| cursor: pointer; | |
| z-index: 1000; | |
| } | |
| input[type="text"] { | |
| background-color: var(--input-bg); | |
| color: var(--text-color); | |
| border: 1px solid var(--border-color); | |
| } | |
| input[type="text"]::placeholder { | |
| color: var(--secondary-color); | |
| } | |
| .btn-primary { | |
| background-color: var(--primary-color); | |
| border-color: var(--primary-color); | |
| } | |
| .btn-primary:hover { | |
| background-color: var(--secondary-color); | |
| border-color: var(--secondary-color); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="app"> | |
| <button @click="toggleDarkMode" class="mode-toggle" :title="isDarkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode'"> | |
| <i :class="isDarkMode ? 'fas fa-sun' : 'fas fa-moon'"></i> | |
| </button> | |
| <div class="chat-outer-container"> | |
| <div class="chat-container"> | |
| <h1 class="title">PitchPerfect</h1> | |
| <h2 class="subtitle">by Elevatics.ai</h2> | |
| <div v-for="(message, index) in chatHistory" :key="index" class="message" :class="message.type === 'user' ? 'user-message' : 'bot-message'"> | |
| <div v-if="message.type === 'bot'" v-html="message.content"></div> | |
| <div v-else>{{ message.content }}</div> | |
| <div v-if="message.html" class="position-relative"> | |
| <div class="position-absolute top-0 end-0 m-2"> | |
| <div class="dropdown"> | |
| <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-expanded="false"> | |
| Download | |
| </button> | |
| <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton"> | |
| <li v-for="format in ['html', 'pdf', 'pptx']" :key="format"> | |
| <a class="dropdown-item" href="#" @click.prevent="downloadPresentation(format, message.markdown_presentation)">{{ format.toUpperCase() }}</a> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| <iframe :srcdoc="message.html" class="presentation-iframe mt-2"></iframe> | |
| </div> | |
| </div> | |
| <div v-if="isLoading" class="text-center"> | |
| <div class="spinner-border loading-spinner" role="status"> | |
| <span class="visually-hidden">Loading...</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="alert alert-danger" v-if="errorMessage">{{ errorMessage }}</div> | |
| <div class="input-container"> | |
| <form @submit.prevent="sendMessage" class="d-flex"> | |
| <input v-model="userInput" type="text" class="form-control me-2" placeholder="Type your message..."> | |
| <button type="submit" class="btn btn-primary" :disabled="isLoading">Send</button> | |
| </form> | |
| <button @click="resetConversation" class="reset-button" title="Reset Conversation"> | |
| <i class="fas fa-redo"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const { createApp, ref, computed } = Vue | |
| createApp({ | |
| setup() { | |
| const userInput = ref('') | |
| const chatHistory = ref([]) | |
| const conversationId = ref('') | |
| const isLoading = ref(false) | |
| const errorMessage = ref('') | |
| const AUTH_TOKEN = "44d5c2ac18ced6fc25c1e57dcd06fc0b31fb4ad97bf56e67540671a647465df4" | |
| const modelId = 'openai/gpt-4o-mini' | |
| const isDarkMode = ref(false) | |
| function resetConversation() { | |
| chatHistory.value = [] | |
| conversationId.value = generateUUID() | |
| errorMessage.value = '' | |
| } | |
| function generateUUID() { | |
| return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) | |
| } | |
| function toggleDarkMode() { | |
| isDarkMode.value = !isDarkMode.value | |
| applyDarkMode() | |
| } | |
| function applyDarkMode() { | |
| if (isDarkMode.value) { | |
| document.body.classList.add('dark-mode') | |
| } else { | |
| document.body.classList.remove('dark-mode') | |
| } | |
| } | |
| async function sendMessage() { | |
| if (!userInput.value.trim()) return | |
| chatHistory.value.push({ type: 'user', content: userInput.value }) | |
| const userMessage = userInput.value | |
| userInput.value = '' | |
| isLoading.value = true | |
| errorMessage.value = '' | |
| const payload = { | |
| prompt: userMessage, | |
| model_id: modelId, | |
| conversation_id: conversationId.value, | |
| user_id: "web_user" | |
| } | |
| try { | |
| const response = await fetch('https://pvanand-audio-chat.hf.space/presentation-agent', { | |
| method: 'POST', | |
| headers: { | |
| 'accept': 'application/json', | |
| 'X-API-Key': AUTH_TOKEN, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify(payload) | |
| }) | |
| if (!response.ok) { | |
| const errorText = await response.text() | |
| throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`) | |
| } | |
| const data = await response.json() | |
| const botMessage = { | |
| type: 'bot', | |
| content: data.response ? marked.parse(data.response) : '', | |
| html: data.html_presentation || null, | |
| markdown_presentation: data.markdown_presentation || null | |
| } | |
| chatHistory.value.push(botMessage) | |
| } catch (error) { | |
| errorMessage.value = `Error: ${error.message}` | |
| setTimeout(() => { | |
| errorMessage.value = '' | |
| }, 5000) | |
| } finally { | |
| isLoading.value = false | |
| } | |
| } | |
| async function downloadPresentation(format, markdown) { | |
| if (!markdown) return | |
| try { | |
| const response = await fetch('https://pvanand-audio-chat.hf.space/convert-md-to-presentation', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'accept': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| markdown: markdown, | |
| output_format: format | |
| }) | |
| }) | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`) | |
| } | |
| let blob | |
| if (format === 'html') { | |
| const data = await response.json() | |
| blob = new Blob([data.html], { type: 'text/html' }) | |
| } else { | |
| blob = await response.blob() | |
| } | |
| const url = URL.createObjectURL(blob) | |
| const a = document.createElement('a') | |
| a.href = url | |
| a.download = `presentation.${format}` | |
| a.click() | |
| URL.revokeObjectURL(url) | |
| } catch (error) { | |
| console.error('Error:', error) | |
| alert(`Failed to download ${format.toUpperCase()}`) | |
| } | |
| } | |
| return { | |
| userInput, | |
| chatHistory, | |
| isLoading, | |
| errorMessage, | |
| isDarkMode, | |
| sendMessage, | |
| resetConversation, | |
| toggleDarkMode, | |
| downloadPresentation | |
| } | |
| } | |
| }).mount('#app') | |
| </script> | |
| <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> | |
| </body> | |
| </html> |