Skip to content

Instantly share code, notes, and snippets.

@ildunari
Last active October 29, 2024 21:02
Show Gist options
  • Select an option

  • Save ildunari/c32894a00ac7b3f4349023570ebdddff to your computer and use it in GitHub Desktop.

Select an option

Save ildunari/c32894a00ac7b3f4349023570ebdddff to your computer and use it in GitHub Desktop.
// TypingMind XML Tag Processor Extension
// Version 2.1
// First, we'll create a small bootstrap loader that will be the entry point
(function() {
const TYPING_MIND_READY_CHECK = {
interval: 100,
timeout: 30000, // 30 seconds total timeout
selectors: {
chatSpace: '[data-element-id="chat-space-middle-part"]',
mainContent: '[data-element-id="main-content-area"]'
}
};
function loadMainScript() {
let startTime = Date.now();
let checkInterval;
// Function to check if Typing Mind is ready
function isTypingMindReady() {
const mainContent = document.querySelector(TYPING_MIND_READY_CHECK.selectors.mainContent);
const chatSpace = document.querySelector(TYPING_MIND_READY_CHECK.selectors.chatSpace);
return mainContent && chatSpace;
}
// Function to initialize once ready
function initialize() {
if (isTypingMindReady()) {
clearInterval(checkInterval);
console.log('[XML-MD Extension] Typing Mind UI detected, initializing...');
// Your existing initialization code here
init();
} else if (Date.now() - startTime > TYPING_MIND_READY_CHECK.timeout) {
clearInterval(checkInterval);
console.error('[XML-MD Extension] Timeout waiting for Typing Mind UI');
}
}
// Start checking for Typing Mind UI
checkInterval = setInterval(initialize, TYPING_MIND_READY_CHECK.interval);
// Also observe DOM changes
const observer = new MutationObserver((mutations, obs) => {
if (isTypingMindReady()) {
obs.disconnect();
initialize();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// Load the script when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadMainScript);
} else {
loadMainScript();
}
})();
const CONFIG = {
debugMode: false,
elementIds: {
chatContainer: '[data-element-id="chat-space-middle-part"]',
aiResponse: '[data-element-id="ai-response"]'
},
maxRetries: 10,
retryInterval: 1000,
tagStyles: {
// Medical/Analysis Tags
veterinary_analysis: {
icon: '🩺',
bgColor: 'rgba(200, 230, 255, 0.1)',
borderColor: '#30363d'
},
assessment: {
icon: '🔍',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
diagnosis: {
icon: '🏥',
bgColor: 'rgba(220, 255, 220, 0.1)',
borderColor: '#30363d'
},
recommendations: {
icon: '✔️',
bgColor: 'rgba(255, 255, 220, 0.1)',
borderColor: '#30363d'
},
patient_info: {
icon: '📋',
bgColor: 'rgba(220, 220, 255, 0.1)',
borderColor: '#30363d'
},
// Status/Alert Tags
warning: {
icon: '⚠️',
bgColor: 'rgba(255, 250, 220, 0.2)',
borderColor: '#8B8000'
},
error: {
icon: '❌',
bgColor: 'rgba(255, 220, 220, 0.2)',
borderColor: '#8B0000'
},
status: {
icon: 'ℹ️',
bgColor: 'rgba(220, 220, 255, 0.1)',
borderColor: '#30363d'
},
message: {
icon: '💬',
bgColor: 'rgba(220, 255, 220, 0.1)',
borderColor: '#30363d'
},
// Document Structure Tags
document: {
icon: '📄',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
section: {
icon: '📑',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
content: {
icon: '📝',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
notes: {
icon: '📌',
bgColor: 'rgba(255, 255, 220, 0.1)',
borderColor: '#30363d'
},
// Technical Tags
code: {
icon: '💻',
bgColor: 'rgba(40, 44, 52, 0.95)',
borderColor: '#30363d'
},
output: {
icon: '📤',
bgColor: 'rgba(40, 44, 52, 0.95)',
borderColor: '#30363d'
},
result: {
icon: '🎯',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
data: {
icon: '📊',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
// Analysis/Review Tags
analysis: {
icon: '🔎',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
evaluation: {
icon: '⚖️',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
summary: {
icon: '📋',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
details: {
icon: '🔍',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
example: {
icon: '💡',
bgColor: 'rgba(255, 255, 220, 0.1)',
borderColor: '#30363d'
},
// Special Tags
thinking: {
icon: '💭',
bgColor: 'rgba(13, 17, 23, 0.7)',
borderColor: '#30363d'
},
explanation: {
icon: '🔍',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
},
note: {
icon: '📝',
bgColor: 'rgba(255, 255, 220, 0.1)',
borderColor: '#30363d'
},
important: {
icon: '❗',
bgColor: 'rgba(255, 220, 220, 0.1)',
borderColor: '#8B0000'
}
}
};
// Add required CSS
const style = document.createElement('style');
style.textContent = `
.xml-block {
margin: 1em 0;
padding: 0.5em;
border-radius: 6px;
border-width: 1px;
border-style: solid;
color: inherit;
transition: all 0.3s ease;
}
.xml-header {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
user-select: none;
padding: 4px;
border-radius: 4px;
font-weight: 500;
}
.xml-header:hover {
background: rgba(255, 255, 255, 0.05);
}
.xml-content {
margin-top: 8px;
padding: 8px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.05);
transition: all 0.3s ease;
}
.xml-block[data-collapsed] .xml-content {
display: none;
}
.xml-block[data-collapsed] .xml-toggle::after {
content: "▶";
transition: transform 0.3s ease;
}
.xml-block:not([data-collapsed]) .xml-toggle::after {
content: "▼";
transition: transform 0.3s ease;
}
.xml-toggle {
margin-left: auto;
}
.xml-tag-name {
text-transform: capitalize;
font-size: 0.9em;
}
/* Special styling for code blocks */
.xml-block[data-tag="code"] .xml-content,
.xml-block[data-tag="output"] .xml-content {
font-family: monospace;
white-space: pre-wrap;
background: rgba(40, 44, 52, 0.95);
}
/* Dark mode specific styles */
@media (prefers-color-scheme: dark) {
.xml-block {
color: #e0e0e0;
}
.xml-header:hover {
background: rgba(255, 255, 255, 0.1);
}
}
`;
// Utility function for debugging
function log(...args) {
if (CONFIG.debugMode) {
console.log('[XML-MD Extension]:', ...args);
}
}
// Debounce function to prevent multiple rapid executions
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Function to wait for element
function waitForElement(selector, callback, retryCount = 0) {
const element = document.querySelector(selector);
if (element) {
log('Found element:', selector);
callback(element);
return;
}
if (retryCount >= CONFIG.maxRetries) {
console.error('[XML-MD Extension]: Chat space not found after maximum retries');
return;
}
log(`Element ${selector} not found, retrying... (${retryCount + 1}/${CONFIG.maxRetries})`);
setTimeout(() => {
waitForElement(selector, callback, retryCount + 1);
}, CONFIG.retryInterval);
}
// Function to check if element is inside a code block
function isInsideCodeBlock(element) {
let current = element;
while (current && current.parentElement) {
const tagName = current.tagName?.toLowerCase();
if (
tagName === 'pre' ||
tagName === 'code' ||
current.classList?.contains('task-list-item') ||
current.classList?.contains('language-') ||
current.closest('.highlight, .syntax-highlight, .code-block')
) {
return true;
}
current = current.parentElement;
}
return false;
}
// Create XML block with proper styling and structure
function createXmlBlock(tagName, content) {
const normalizedTagName = tagName.toLowerCase();
const style = CONFIG.tagStyles[normalizedTagName] || {
icon: '📄',
bgColor: 'rgba(255, 255, 255, 0.05)',
borderColor: '#30363d'
};
const block = document.createElement('div');
block.className = 'xml-block';
block.dataset.tag = normalizedTagName;
block.style.backgroundColor = style.bgColor;
block.style.borderColor = style.borderColor;
const header = document.createElement('div');
header.className = 'xml-header';
header.innerHTML = `
<span>${style.icon}</span>
<span class="xml-tag-name">${normalizedTagName.replace(/_/g, ' ')}</span>
<span class="xml-toggle"></span>
`;
const contentDiv = document.createElement('div');
contentDiv.className = 'xml-content';
// Handle content based on tag type
if (normalizedTagName === 'code' || normalizedTagName === 'output') {
contentDiv.innerHTML = `<pre><code>${content.trim()}</code></pre>`;
} else {
contentDiv.innerHTML = content.trim();
}
block.appendChild(header);
block.appendChild(contentDiv);
// Add click handler for collapsing/expanding
header.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
block.toggleAttribute('data-collapsed');
});
return block;
}
// Process individual element for XML tags
function processElement(element) {
if (element.hasAttribute('data-xml-processed')) {
return;
}
function processNode(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
// Skip processing for code blocks and syntax highlighting elements
if (node.tagName === 'PRE' ||
node.tagName === 'CODE' ||
node.classList.contains('syntax-highlight') ||
node.classList.contains('language-')) {
return;
}
Array.from(node.childNodes).forEach(processNode);
}
else if (node.nodeType === Node.TEXT_NODE) {
const text = node.textContent;
let pattern = /<(\w+)>([\s\S]*?)<\/\1>/g;
let matches = [...text.matchAll(pattern)];
if (matches.length > 0) {
const fragment = document.createDocumentFragment();
let lastIndex = 0;
matches.forEach(match => {
const [fullMatch, tagName, content] = match;
const startIndex = text.indexOf(fullMatch, lastIndex);
// Add text before the XML tag
if (startIndex > lastIndex) {
fragment.appendChild(
document.createTextNode(
text.substring(lastIndex, startIndex)
)
);
}
// Add the XML block if there's content
if (content.trim()) {
fragment.appendChild(
createXmlBlock(tagName.toLowerCase(), content)
);
}
lastIndex = startIndex + fullMatch.length;
});
// Add any remaining text
if (lastIndex < text.length) {
fragment.appendChild(
document.createTextNode(
text.substring(lastIndex)
)
);
}
node.parentNode.replaceChild(fragment, node);
}
}
}
try {
processNode(element);
element.setAttribute('data-xml-processed', 'true');
} catch (error) {
console.error('[XML-MD Extension]: Error processing element:', error);
}
}
// Initialize the extension
function init() {
log('Initializing XML-MD Extension');
const observer = new MutationObserver(
debounce((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.matches(CONFIG.elementIds.aiResponse)) {
processElement(node);
}
node.querySelectorAll(CONFIG.elementIds.aiResponse)
.forEach(processElement);
}
});
});
}, 100)
);
// Wait for chat container and initialize
waitForElement(CONFIG.elementIds.chatContainer, (chatContainer) => {
// Add styles to document
if (!document.querySelector('style[data-xml-md-styles]')) {
style.setAttribute('data-xml-md-styles', 'true');
document.head.appendChild(style);
}
// Start observing chat container
observer.observe(chatContainer, {
childList: true,
subtree: true
});
// Process any existing messages
document.querySelectorAll(CONFIG.elementIds.aiResponse)
.forEach(processElement);
log('Extension initialized successfully');
});
}
// Start the extension when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment