Skip to content

Instantly share code, notes, and snippets.

@AndyCarnevale
Last active March 22, 2024 15:50
Show Gist options
  • Save AndyCarnevale/1d65bc61f52227692aff50af39a81e04 to your computer and use it in GitHub Desktop.
Save AndyCarnevale/1d65bc61f52227692aff50af39a81e04 to your computer and use it in GitHub Desktop.
Claude Chat Formatter
/**
* Claude Chat Formatter
*
* This script is designed to enhance the readability and distinction of messages in the Claude chat interface by either
* prepending labels to messages or wrapping messages in custom XML-like tags. It's particularly useful for clearly
* distinguishing between user and AI messages, making it easier to copy and paste the conversation with clear attribution.
*
* Functionality:
* - `prependLabels()`: Prepends a label indicating "user:" or "ai:" at the start of each message.
* - `tagMessages()`: Wraps each message with custom XML-like tags, <user>...</user> for user messages and <ai>...</ai> for AI messages.
* - `clearUsernameDivText()`: Clears the text contained within username div elements.
*
* The script uses MutationObserver to dynamically apply these formatting options to new messages as they appear in the chat,
* ensuring that all messages, including those loaded or added after the initial page load, are formatted correctly.
*
* Configuration:
* - The script allows easy switching between the two formatting functions (`prependLabels` and `tagMessages`) through
* the `applyFunction` variable. By default, it is set to use `tagMessages`. To switch to `prependLabels`, simply assign
* it to `applyFunction` like so: `const applyFunction = prependLabels;`.
*
* Usage:
* - To use the script, choose the desired formatting function by setting the `applyFunction` variable.
* - Add this script to the webpage with the AI chat interface.
*/
// Configuration: Directly assign the function to use
// const applyFunction = prependLabels;
const applyFunction = tagMessages;
// Function to prepend labels to messages
function prependLabels() {
// Select all human messages and prepend a label if not already done
const userMessages = document.querySelectorAll('.font-user-message:not(.label-added)');
userMessages.forEach(message => {
const label = document.createElement('span'); // Create a new span element for the label
label.textContent = 'user: '; // Set text content
label.style.fontWeight = 'bold'; // Optional: make the label bold
// Insert the label at the beginning of the message div
message.insertBefore(label, message.firstChild);
// Mark this message as having a label added to avoid duplicates
message.classList.add('label-added');
});
// Select all AI messages and prepend a label if not already done
const aiMessages = document.querySelectorAll('.font-claude-message:not(.label-added)');
aiMessages.forEach(message => {
const label = document.createElement('span'); // Create a new span element for the label
label.textContent = 'ai: '; // Set text content
label.style.fontWeight = 'bold'; // Optional: make the label bold
// Insert the label at the beginning of the message div
message.insertBefore(label, message.firstChild);
// Mark this message as having a label added to avoid duplicates
message.classList.add('label-added');
});
}
// Function to add opening and closing tags around messages
function tagMessages() {
// Select all human messages and tag them if not already done
const userMessages = document.querySelectorAll('.font-user-message:not(.tagged)');
userMessages.forEach(message => {
// Create opening and closing tags
const openTag = document.createElement('div');
openTag.textContent = '<user>';
const closeTag = document.createElement('div');
closeTag.textContent = '</user>';
// Insert opening tag at the beginning
message.insertBefore(openTag, message.firstChild);
// Append closing tag at the end
message.appendChild(closeTag);
// Mark this message to avoid re-tagging
message.classList.add('tagged');
});
// Select all AI messages and tag them if not already done
const aiMessages = document.querySelectorAll('.font-claude-message:not(.tagged)');
aiMessages.forEach(message => {
// Create opening and closing tags
const openTag = document.createElement('div');
openTag.textContent = '<ai>';
const closeTag = document.createElement('div');
closeTag.textContent = '</ai>';
// Insert opening tag at the beginning
message.insertBefore(openTag, message.firstChild);
// Append closing tag at the end
message.appendChild(closeTag);
// Mark this message to avoid re-tagging
message.classList.add('tagged');
});
}
function clearUsernameDivText() {
// Select all elements with the specific class pattern - should just be the username
const elements = document.querySelectorAll('.font-bold');
elements.forEach(element => {
element.textContent = ''; // Clear the text content of each element
});
}
// MutationObserver callback to prepend labels when new nodes are added
const observerCallback = (mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
applyFunction(); // Directly call the function assigned to applyFunction
clearUsernameDivText(); // Clear text of specific divs
}
}
};
// Options for the observer (which mutations to observe)
const observerOptions = {
childList: true,
subtree: true
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(observerCallback);
// Start observing the target node for configured mutations
const targetNode = document.body; // You might need to target a more specific part of the page
observer.observe(targetNode, observerOptions);
// Initially apply the chosen method
applyFunction(); // Directly call the function assigned to applyFunction
clearUsernameDivText
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment