Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <html><head><base href="https://claude.ai"><title>Claude AI Chat</title>
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css">
- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/javascript/javascript.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/python/python.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/xml/xml.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/css/css.min.js"></script>
- <style>
- /* Styles remain unchanged */
- body, html {
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
- line-height: 1.5;
- color: #343541;
- margin: 0;
- padding: 0;
- height: 100%;
- overflow: hidden;
- }
- .container {
- display: flex;
- height: 100vh;
- }
- .sidebar {
- width: 50%;
- max-width: 120px;
- background-color: #f7f7f8;
- border-right: 1px solid #e5e5e5;
- padding: 5px;
- overflow-y: auto;
- }
- .main-content {
- flex-grow: 1;
- display: flex;
- flex-direction: column;
- background-color: #ffffff;
- }
- .chat-window {
- flex-grow: 1;
- overflow-y: auto;
- padding: 5px;
- }
- .message {
- width: 100%;
- max-width: 1080px;
- margin-bottom: 5px;
- padding: 5px 5px;
- border-radius: 5px;
- line-height: 1.4;
- }
- .user-message {
- background-color: #f0f6ff;
- color: #000000;
- align-self: flex-end;
- margin-left: auto;
- }
- .ai-message {
- background-color: #f7f7f8;
- color: #000000;
- }
- .input-area {
- background-color: #ffffff;
- padding: 5px;
- border-top: 1px solid #e5e5e5;
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex-wrap: wrap;
- }
- .user-input {
- flex-grow: 1;
- min-width: 1080;
- padding: 5px;
- border-radius: 5px;
- border: 1px solid #e5e5e5;
- background-color: #ffffff;
- color: #000000;
- font-size: 16px;
- resize: none;
- }
- .sidebar h2 {
- color: #000000;
- font-size: 20px;
- margin-bottom: 5px;
- }
- .sidebar ul {
- list-style-type: none;
- padding: 0;
- }
- .sidebar li {
- margin-bottom: 5px;
- }
- .sidebar a {
- color: #000000;
- text-decoration: none;
- cursor: pointer;
- }
- .error-message {
- color: #ff4444;
- margin-top: 5px;
- }
- .new-chat-btn, .upload-btn {
- background-color: #10a37f;
- color: white;
- border: none;
- padding: 5px;
- border-radius: 5px;
- cursor: pointer;
- width: 100%;
- margin-bottom: 5px;
- }
- .loading {
- display: none;
- text-align: center;
- padding: 5px;
- }
- .loading::after {
- content: '...';
- animation: dots 1s steps(5, end) infinite;
- }
- @keyframes dots {
- 0%, 20% {
- color: rgba(0,0,0,0);
- text-shadow:
- .25em 0 0 rgba(0,0,0,0),
- .5em 0 0 rgba(0,0,0,0);
- }
- 40% {
- color: #000;
- text-shadow:
- .25em 0 0 rgba(0,0,0,0),
- .5em 0 0 rgba(0,0,0,0);
- }
- 60% {
- text-shadow:
- .25em 0 0 #000,
- .5em 0 0 rgba(0,0,0,0);
- }
- 80%, 100% {
- text-shadow:
- .25em 0 0 #000,
- .5em 0 0 #000;
- }
- }
- .CodeMirror {
- height: auto;
- border: 1px solid #eee;
- margin-bottom: 5px;
- }
- #file-input {
- display: none;
- }
- .cancel-btn {
- background-color: #ff4444;
- color: white;
- border: none;
- padding: 5px;
- border-radius: 5px;
- cursor: pointer;
- margin-top: 5px;
- display: none;
- }
- .timer-container {
- display: flex;
- align-items: center;
- margin-top: 5px;
- }
- .timer {
- margin-left: 5px;
- font-size: 14px;
- color: #666;
- }
- .floating-menu {
- position: fixed;
- background-color: #fff;
- border: 1px solid #ccc;
- border-radius: 10px;
- padding: 5px;
- z-index: 1000;
- }
- .rounded-menu {
- position: fixed;
- background-color: #fff;
- border: 1px solid #ccc;
- border-radius: 10px;
- padding: 5px;
- z-index: 1000;
- display: flex;
- flex-direction: column;
- align-items: center;
- }
- .menu-item {
- cursor: pointer;
- padding: 5px;
- border-radius: 10px;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="sidebar">
- <button class="new-chat-btn" onclick="newChat()">New chat</button>
- <input type="file" id="file-input" accept="*/*" onchange="uploadFile()" style="display: none;">
- <button class="upload-btn" onclick="document.getElementById('file-input').click()">Upload File</button>
- <h2>Recent chats</h2>
- <ul id="recent-chats">
- <!-- Recent chats will be dynamically added here -->
- </ul>
- </div>
- <div class="main-content">
- <div class="chat-window" id="chat-window">
- <div class="message ai-message">
- <p>Hello! How can I assist you today?</p>
- </div>
- </div>
- <div class="input-area">
- <form id="chat-form">
- <input type="hidden" name="origin" value="https://claude.ai">
- <textarea name="message" class="user-input" placeholder="Message Claude..." rows="1"></textarea>
- </form>
- <div class="timer-container">
- <button id="cancel-btn" class="cancel-btn" style="display: none;">Cancel</button>
- <div id="timer" class="timer"></div>
- </div>
- <div class="loading">Claude Is Thinking</div>
- <div id="error-message" class="error-message"></div>
- </div>
- </div>
- </div>
- <script>
- const chatWindow = document.getElementById('chat-window');
- const chatForm = document.getElementById('chat-form');
- const userInput = chatForm.querySelector('textarea[name="message"]');
- const loadingIndicator = document.querySelector('.loading');
- const errorMessage = document.getElementById('error-message');
- const recentChats = document.getElementById('recent-chats');
- const cancelBtn = document.getElementById('cancel-btn');
- const timerDisplay = document.getElementById('timer');
- let chatHistories = {};
- let currentChatId = Date.now();
- let isRequestCancelled = false;
- let timerInterval;
- let startTime;
- chatForm.addEventListener('submit', function(event) {
- event.preventDefault();
- sendMessage();
- });
- cancelBtn.addEventListener('click', function() {
- isRequestCancelled = true;
- loadingIndicator.style.display = 'none';
- cancelBtn.style.display = 'none';
- clearInterval(timerInterval);
- timerDisplay.textContent = '';
- });
- function startTimer() {
- startTime = Date.now();
- timerInterval = setInterval(updateTimer, 1000);
- updateTimer();
- }
- function updateTimer() {
- const elapsedTime = Math.floor((Date.now() - startTime) / 1000);
- timerDisplay.textContent = `${elapsedTime}s`;
- }
- function stopTimer() {
- clearInterval(timerInterval);
- timerDisplay.textContent = '';
- }
- function uploadFile() {
- const fileInput = document.getElementById('file-input');
- const file = fileInput.files[0];
- if (file) {
- const reader = new FileReader();
- reader.onload = function(e) {
- const fileContent = e.target.result;
- const fileSizeKB = Math.round(file.size / 1024);
- sendFileToAI(file.name, fileContent, fileSizeKB);
- };
- if (file.type.startsWith('image/')) {
- reader.readAsDataURL(file);
- } else {
- reader.readAsText(file);
- }
- }
- }
- function sendFileToAI(fileName, fileContent, fileSizeKB) {
- let messageContent;
- if (fileContent.startsWith('data:image')) {
- messageContent = `<action>
- {
- "type": "file_upload",
- "name": "${fileName}",
- "content_type": "${fileContent.split(';')[0].split(':')[1]}",
- "base64_content": "${fileContent.split(',')[1]}",
- "size_kb": ${fileSizeKB}
- }
- </action>`;
- } else {
- messageContent = `<action>
- {
- "type": "file_upload",
- "name": "${fileName}",
- "content": ${JSON.stringify(fileContent)},
- "size_kb": ${fileSizeKB}
- }
- </action>`;
- }
- // Send the full content to the AI (not visible to the user)
- sendMessage(messageContent, true);
- // Display only the file name and size to the user
- const userVisibleMessage = `Uploaded file: ${fileName} (${fileSizeKB} KB)`;
- appendMessage('user', userVisibleMessage);
- }
- async function sendMessage(overrideMessage = null, isFileUpload = false) {
- const message = overrideMessage || userInput.value.trim();
- if (message === '') return;
- if (!isFileUpload) {
- appendMessage('user', message);
- }
- userInput.value = '';
- loadingIndicator.style.display = 'block';
- cancelBtn.style.display = 'inline-block';
- errorMessage.textContent = '';
- isRequestCancelled = false;
- startTimer();
- while (!isRequestCancelled) {
- try {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
- const response = await fetch('/api/chat', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ message: message, origin: 'https://claude.ai' }),
- signal: controller.signal
- });
- clearTimeout(timeoutId);
- if (response.ok) {
- const data = await response.json();
- appendMessage('ai', data.response);
- break; // Exit the loop if we get a successful response
- } else {
- throw new Error('API response was not ok');
- }
- } catch (error) {
- // Silently ignore errors and continue the loop
- await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for 1 second before retrying
- }
- }
- loadingIndicator.style.display = 'none';
- cancelBtn.style.display = 'none';
- stopTimer();
- }
- function appendMessage(sender, content) {
- const messageDiv = document.createElement('div');
- messageDiv.className = `message ${sender}-message`;
- // Parse content for code blocks
- const parsedContent = parseCodeBlocks(content);
- messageDiv.innerHTML = parsedContent;
- chatWindow.appendChild(messageDiv);
- chatWindow.scrollTop = chatWindow.scrollHeight;
- // Initialize CodeMirror for code blocks
- messageDiv.querySelectorAll('pre code').forEach((block) => {
- const mode = block.className.replace('language-', '');
- CodeMirror(function(elt) {
- block.parentNode.replaceChild(elt, block);
- }, {
- value: block.textContent,
- mode: mode,
- lineNumbers: true,
- readOnly: true,
- theme: 'default'
- });
- });
- // Add message to current chat history
- if (!chatHistories[currentChatId]) {
- chatHistories[currentChatId] = [];
- }
- chatHistories[currentChatId].push({ sender, content });
- }
- function parseCodeBlocks(content) {
- const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
- return content.replace(codeBlockRegex, (match, language, code) => {
- return `<pre><code class="language-${language || 'plaintext'}">${escapeHtml(code.trim())}</code></pre>`;
- });
- }
- function escapeHtml(unsafe) {
- return unsafe
- .replace(/&/g, "&")
- .replace(/</g, "<")
- .replace(/>/g, ">")
- .replace(/"/g, """)
- .replace(/'/g, "'");
- }
- function newChat() {
- // Save current chat to history if it's not empty
- if (chatHistories[currentChatId] && chatHistories[currentChatId].length > 0) {
- const chatPreview = chatHistories[currentChatId][0].content.substring(0, 30) + '...';
- addRecentChat(currentChatId, chatPreview);
- }
- // Clear current chat window
- chatWindow.innerHTML = '<div class="message ai-message"><p>Hello! How can I assist you today?</p></div>';
- currentChatId = Date.now();
- userInput.value = '';
- errorMessage.textContent = '';
- }
- function addRecentChat(id, preview) {
- const li = document.createElement('li');
- li.innerHTML = `<a onclick="loadChat(${id})">${preview}</a>`;
- recentChats.insertBefore(li, recentChats.firstChild);
- // Limit to 5 recent chats
- if (recentChats.children.length > 5) {
- recentChats.removeChild(recentChats.lastChild);
- }
- }
- function loadChat(chatId) {
- if (chatHistories[chatId]) {
- chatWindow.innerHTML = ''; // Clear current chat window
- chatHistories[chatId].forEach(message => {
- appendMessage(message.sender, message.content);
- });
- chatWindow.scrollTop = chatWindow.scrollHeight;
- currentChatId = chatId;
- } else {
- chatWindow.innerHTML = '<div class="message ai-message"><p>This chat history is not available.</p></div>';
- }
- userInput.value = '';
- errorMessage.textContent = '';
- }
- userInput.addEventListener('keydown', function(event) {
- if (event.key === 'Enter' && !event.shiftKey) {
- event.preventDefault();
- sendMessage();
- }
- });
- userInput.addEventListener('input', function() {
- this.style.height = 'auto';
- this.style.height = this.scrollHeight + 'px';
- });
- // Function to handle right-click on chatbot bubble
- function handleChatbotBubbleRightClick(event, messageDiv) {
- event.preventDefault();
- const codeBlock = messageDiv.querySelector('pre code');
- if (codeBlock) {
- // Code window exists, show "Copy Only This Code" option
- showFloatingMenu(event.clientX, event.clientY, [
- { label: 'Copy Only This Code', action: () => copyCode(codeBlock) }
- ]);
- } else {
- // No code window, show "Copy This Reply" option
- showRoundedMenu(event.clientX, event.clientY, [
- { label: 'Copy This Reply', action: () => copyText(messageDiv.textContent) }
- ]);
- }
- }
- // Function to handle right-click on user bubble
- function handleUserBubbleRightClick(event, messageDiv) {
- event.preventDefault();
- showRoundedMenu(event.clientX, event.clientY, [
- { label: 'Edit', action: () => editMessage(messageDiv) }
- ]);
- }
- // Function to show floating menu
- function showFloatingMenu(x, y, options) {
- const menu = document.createElement('div');
- menu.className = 'floating-menu';
- menu.style.left = x + 'px';
- menu.style.top = y + 'px';
- options.forEach(option => {
- const menuItem = document.createElement('div');
- menuItem.className = 'menu-item';
- menuItem.textContent = option.label;
- menuItem.addEventListener('click', option.action);
- menu.appendChild(menuItem);
- });
- document.body.appendChild(menu);
- // Close the menu when clicking outside
- document.addEventListener('click', closeFloatingMenu);
- }
- // Function to close floating menu
- function closeFloatingMenu() {
- const menu = document.querySelector('.floating-menu');
- if (menu) {
- menu.remove();
- document.removeEventListener('click', closeFloatingMenu);
- }
- }
- // Function to show rounded menu
- function showRoundedMenu(x, y, options) {
- const menu = document.createElement('div');
- menu.className = 'rounded-menu';
- menu.style.left = x + 'px';
- menu.style.top = y + 'px';
- options.forEach(option => {
- const menuItem = document.createElement('div');
- menuItem.className = 'menu-item';
- menuItem.textContent = option.label;
- menuItem.addEventListener('click', option.action);
- menu.appendChild(menuItem);
- });
- document.body.appendChild(menu);
- // Close the menu when clicking outside
- document.addEventListener('click', closeRoundedMenu);
- }
- // Function to close rounded menu
- function closeRoundedMenu() {
- const menu = document.querySelector('.rounded-menu');
- if (menu) {
- menu.remove();
- document.removeEventListener('click', closeRoundedMenu);
- }
- }
- // Function to copy code
- function copyCode(codeBlock) {
- const textarea = document.createElement('textarea');
- textarea.value = codeBlock.textContent;
- document.body.appendChild(textarea);
- textarea.select();
- document.execCommand('copy');
- document.body.removeChild(textarea);
- }
- // Function to copy text
- function copyText(text) {
- const textarea = document.createElement('textarea');
- textarea.value = text;
- document.body.appendChild(textarea);
- textarea.select();
- document.execCommand('copy');
- document.body.removeChild(textarea);
- }
- // Function to edit message
- function editMessage(messageDiv) {
- const messageContent = messageDiv.textContent;
- userInput.value = messageContent;
- // Delete all the text bubbles under the selected bubble
- let nextSibling = messageDiv.nextSibling;
- while (nextSibling) {
- const nextMessageDiv = nextSibling;
- nextSibling = nextSibling.nextSibling;
- chatWindow.removeChild(nextMessageDiv);
- }
- // Delete the selected text bubble
- chatWindow.removeChild(messageDiv);
- }
- // Add event listeners for right-click on chatbot and user bubbles
- chatWindow.addEventListener('contextmenu', event => {
- const messageDiv = event.target.closest('.message');
- if (messageDiv && messageDiv.classList.contains('ai-message')) {
- handleChatbotBubbleRightClick(event, messageDiv);
- } else if (messageDiv && messageDiv.classList.contains('user-message')) {
- handleUserBubbleRightClick(event, messageDiv);
- }
- });
- </script>
- </body>
- </html>
- </script>
- </body>
- </html>
Add Comment
Please, Sign In to add comment