Last active
October 29, 2024 21:02
-
-
Save ildunari/c32894a00ac7b3f4349023570ebdddff to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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