Spaces:
Running
Running
| <html> | |
| <head> | |
| <title>ReasonGraph</title> | |
| <link rel="icon" href="static/assets/idea.png" type="image/x-icon"> | |
| <link rel="shortcut icon" href="favicon.ico" type="image/x-icon"> | |
| <script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script> | |
| <style> | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| background-color: #f5f5f5; | |
| } | |
| .banner { | |
| margin: 3px 20px; | |
| border-radius: 8px; | |
| background-image: url("{{ url_for('static', filename='assets/banner-bg.jpg') }}"); | |
| background-size: cover; | |
| background-position: center; | |
| box-shadow: 0 1px 3px rgba(0,0,0,0.1); | |
| height: 280px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: relative; | |
| } | |
| .banner::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.4); | |
| border-radius: 8px; | |
| } | |
| .banner-content { | |
| position: relative; | |
| z-index: 1; | |
| text-align: center; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 24px; | |
| width: 100%; | |
| max-width: 800px; | |
| padding: 0 20px; | |
| } | |
| .banner h1 { | |
| color: white; | |
| font-size: 32px; | |
| font-weight: 600; | |
| margin: 0; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.2); | |
| } | |
| .search-area { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 16px; | |
| width: 100%; | |
| } | |
| .search-input-container { | |
| position: relative; | |
| width: 100%; | |
| } | |
| .search-input { | |
| width: 100%; | |
| padding: 12px; | |
| border: 2px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 8px; | |
| background: rgba(255, 255, 255, 0.9); | |
| font-size: 16px; | |
| resize: none; | |
| outline: none; | |
| transition: border-color 0.2s; | |
| } | |
| .search-input:focus { | |
| border-color: rgba(255, 255, 255, 0.5); | |
| } | |
| .search-buttons { | |
| display: flex; | |
| gap: 10px; | |
| justify-content: center; | |
| align-items: center; | |
| margin: 0 auto; | |
| max-width: 800px; /* Match search input max-width */ | |
| width: 100%; | |
| padding: 0 20px; | |
| } | |
| .search-buttons .param-input { | |
| width: 200px ; /* Override the default param-input width */ | |
| padding: 8px 16px; | |
| background-color: rgba(255, 255, 255, 0.9); | |
| border: none; | |
| border-radius: 6px; | |
| font-size: 14px; | |
| font-weight: 500; | |
| height: 35px; /* Match button height */ | |
| flex: none; /* Override flex property */ | |
| } | |
| .search-buttons button { | |
| width: auto; | |
| min-width: 120px; | |
| padding: 8px 16px; | |
| background-color: rgba(37, 99, 235, 0.8); /* Semi-transparent gray */ | |
| color: white; /* White text */ | |
| border: none; | |
| border-radius: 6px; | |
| font-size: 14px; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| height: 35px; /* Fixed height */ | |
| line-height: 1; /* Ensure text vertical centering */ | |
| } | |
| .search-buttons button:hover { | |
| background-color: rgba(37, 99, 235, 0.9); /* 鼠标悬停时变为深一点的蓝色 */ | |
| transform: translateY(-1px); | |
| } | |
| .links { | |
| display: flex; | |
| justify-content: center; | |
| gap: 0; | |
| white-space: nowrap; | |
| } | |
| .links a { | |
| color: white; | |
| text-decoration: none; | |
| font-size: 16px; | |
| opacity: 0.9; | |
| transition: opacity 0.2s; | |
| } | |
| .links a:hover { | |
| opacity: 1; | |
| text-decoration: underline; | |
| } | |
| .container { | |
| display: flex; | |
| min-height: 100vh; | |
| gap: 20px; | |
| padding: 20px; | |
| } | |
| .column { | |
| flex: 1; | |
| background: white; | |
| border-radius: 8px; | |
| box-shadow: 0 1px 3px rgba(0,0,0,0.1); | |
| padding: 20px; | |
| } | |
| h2 { | |
| margin-top: 0; | |
| margin-bottom: 20px; | |
| color: #1f2937; | |
| font-size: 18px; | |
| font-weight: 600; | |
| } | |
| .param-group { | |
| display: flex; | |
| margin-bottom: 15px; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 4px; | |
| overflow: hidden; | |
| } | |
| .param-label { | |
| width: 180px; | |
| padding: 10px 15px; | |
| background-color: #f8f9fa; | |
| border-right: 1px solid #e5e7eb; | |
| font-size: 14px; | |
| line-height: 1.5; | |
| color: #374151; | |
| } | |
| .param-input { | |
| flex: 1; | |
| padding: 10px 15px; | |
| border: none; | |
| font-size: 14px; | |
| line-height: 1.5; | |
| outline: none; | |
| background: white; | |
| } | |
| select.param-input { | |
| cursor: pointer; | |
| padding-right: 30px; | |
| appearance: none; | |
| background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%236b7280' viewBox='0 0 16 16'%3E%3Cpath d='M8 10l4-4H4l4 4z'/%3E%3C/svg%3E"); | |
| background-repeat: no-repeat; | |
| background-position: right 10px center; | |
| } | |
| textarea.param-input { | |
| resize: vertical; | |
| min-height: 80px; | |
| } | |
| .error-message { | |
| color: #dc2626; | |
| font-size: 14px; | |
| display: none; | |
| position: absolute; | |
| right: 0; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| background: white; | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| box-shadow: 0 1px 2px rgba(0,0,0,0.1); | |
| margin-right: 10px; | |
| } | |
| .search-input-container, .param-group { | |
| position: relative; | |
| } | |
| .output-section { | |
| margin-top: 20px; | |
| padding: 0; | |
| background: white; | |
| } | |
| .output-section h3 { | |
| margin: 0 0 15px 0; | |
| color: #1f2937; | |
| font-size: 18px; | |
| font-weight: 600; | |
| } | |
| .output-wrapper { | |
| overflow: auto; | |
| height: 100px; | |
| min-height: 100px; | |
| max-height: 1000px; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 4px; | |
| padding: 15px; | |
| resize: vertical; | |
| background-color: #f8f9fa; | |
| } | |
| #raw-output { | |
| white-space: pre-wrap; | |
| word-break: break-word; | |
| margin: 0; | |
| font-family: 'Menlo', 'Monaco', 'Courier New', monospace; | |
| font-size: 13px; | |
| line-height: 1.5; | |
| color: #1f2937; | |
| } | |
| button:disabled { | |
| background-color: #9ca3af; | |
| cursor: not-allowed; | |
| } | |
| .zoom-controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| margin-bottom: 10px; | |
| } | |
| .zoom-button { | |
| padding: 5px 10px; | |
| background-color: #f3f4f6; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| color: #374151; | |
| width: auto; | |
| } | |
| .zoom-button:hover { | |
| background-color: #e5e7eb; | |
| } | |
| .zoom-level { | |
| font-size: 14px; | |
| color: #374151; | |
| min-width: 50px; | |
| text-align: center; | |
| } | |
| #mermaid-container { | |
| transform-origin: top left; /* Changed from 'top center' */ | |
| transition: transform 0.2s ease; | |
| width: 100%; | |
| display: block; /* Changed from 'flex' */ | |
| justify-content: flex-start; /* Changed from 'center' */ | |
| } | |
| .visualization-wrapper { | |
| overflow: auto; | |
| height: 370px; | |
| min-height: 100px; | |
| max-height: 1000px; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 4px; | |
| padding: 0; | |
| resize: vertical; | |
| background-color: #f8f9fa; | |
| } | |
| .mermaid { | |
| padding: 0; | |
| border-radius: 4px; | |
| } | |
| .has-visualization .placeholder-visualization { | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="banner"> | |
| <div class="banner-content"> | |
| <h1>ReasonGraph: Visualisation of Reasoning Paths</h1> | |
| <div class="search-area"> | |
| <div class="search-input-container"> | |
| <textarea id="question" class="search-input" placeholder="Enter your question here..." rows="1"></textarea> | |
| <div class="error-message" id="question-error">Please enter a question</div> | |
| </div> | |
| <div class="search-buttons"> | |
| <select class="param-input" id="reasoning-method"> | |
| <!-- Populated dynamically --> | |
| </select> | |
| <button onclick="metaReasoning()" id="meta-btn">Meta Reasoning</button> | |
| <button onclick="processQuestion()" id="process-btn">Start Reasoning</button> | |
| </div> | |
| </div> | |
| <div class="links"> | |
| <a href="https://github.com/ZongqianLi/ReasonGraph"><u>Github</u> | </a><a href="https://arxiv.org/abs/2503.03979"><u>Paper</u> | </a><a href="mailto:zongqianli.work@outlook.com"><u>Email</u></a> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="container"> | |
| <div class="column"> | |
| <h2>Reasoning Settings</h2> | |
| <div class="param-group"> | |
| <div class="param-label">API Provider</div> | |
| <select class="param-input" id="api-provider" onchange="handleProviderChange(this.value)"> | |
| <!-- Populated dynamically --> | |
| </select> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label">Model</div> | |
| <select class="param-input" id="model"> | |
| <!-- Populated dynamically --> | |
| </select> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label">Max Tokens</div> | |
| <input type="number" class="param-input" id="max-tokens"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label">API Key</div> | |
| <input type="password" class="param-input" id="api-key"> | |
| <div class="error-message" id="api-key-error">Please enter a valid API key</div> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label">Custom Prompt Format</div> | |
| <textarea class="param-input" id="prompt-format" rows="6"></textarea> | |
| </div> | |
| <div class="output-section"> | |
| <h3>Raw Model Output</h3> | |
| <div class="output-wrapper"> | |
| <pre id="raw-output">Output will appear here...</pre> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="column"> | |
| <h2>Visualization Settings</h2> | |
| <div class="param-group"> | |
| <div class="param-label">Characters Per Line</div> | |
| <input type="number" class="param-input" id="chars-per-line"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label">Maximum Lines</div> | |
| <input type="number" class="param-input" id="max-lines"> | |
| </div> | |
| <div class="output-section"> | |
| <h3>Visualization Results</h3> | |
| <div class="zoom-controls"> | |
| <button class="zoom-button" onclick="adjustZoom(-0.1)">-</button> | |
| <div class="zoom-level" id="zoom-level">100%</div> | |
| <button class="zoom-button" onclick="adjustZoom(0.1)">+</button> | |
| <button class="zoom-button" onclick="resetZoom()">Reset</button> | |
| <button class="zoom-button" onclick="downloadDiagram()">Download</button> | |
| </div> | |
| <div class="visualization-wrapper"> | |
| <div id="mermaid-container"> | |
| <div id="mermaid-diagram"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Initialize Mermaid | |
| mermaid.initialize({ | |
| startOnLoad: true, | |
| theme: 'default', | |
| securityLevel: 'loose', | |
| flowchart: { | |
| curve: 'basis', | |
| padding: 15 | |
| } | |
| }); | |
| // Store current configuration | |
| let currentConfig = null; | |
| // Zoom control variables | |
| let currentZoom = 1; | |
| const MIN_ZOOM = 0.1; | |
| const MAX_ZOOM = 5; | |
| // Initialize zoom lock flag | |
| window.isZoomLocked = false; | |
| // Handle API Provider change | |
| async function handleProviderChange(provider) { | |
| try { | |
| // Update model list | |
| updateModelList(); | |
| // Get and update API key | |
| const response = await fetch(`/provider-api-key/${provider}`); | |
| const result = await response.json(); | |
| if (result.success) { | |
| document.getElementById('api-key').value = result.api_key; | |
| } else { | |
| console.error('Failed to get API key:', result.error); | |
| } | |
| } catch (error) { | |
| console.error('Error updating provider settings:', error); | |
| } | |
| } | |
| // Load initial configuration | |
| async function loadConfig() { | |
| try { | |
| const response = await fetch('/config'); | |
| currentConfig = await response.json(); | |
| // Populate API providers | |
| const providerSelect = document.getElementById('api-provider'); | |
| currentConfig.general.providers.forEach(provider => { | |
| const option = document.createElement('option'); | |
| option.value = provider; | |
| option.textContent = provider.charAt(0).toUpperCase() + provider.slice(1); | |
| providerSelect.appendChild(option); | |
| }); | |
| // Populate reasoning methods | |
| const methodSelect = document.getElementById('reasoning-method'); | |
| Object.entries(currentConfig.methods).forEach(([id, methodConfig]) => { | |
| const option = document.createElement('option'); | |
| option.value = id; | |
| option.textContent = methodConfig.name; | |
| methodSelect.appendChild(option); | |
| }); | |
| // Initial provider setup | |
| await handleProviderChange(currentConfig.general.providers[0]); | |
| // Set other initial values | |
| document.getElementById('max-tokens').value = currentConfig.general.max_tokens; | |
| document.getElementById('chars-per-line').value = currentConfig.general.visualization.chars_per_line; | |
| document.getElementById('max-lines').value = currentConfig.general.visualization.max_lines; | |
| // Set initial prompt format and example question | |
| const defaultMethod = methodSelect.value; | |
| const methodConfig = currentConfig.methods[defaultMethod]; | |
| updatePromptFormat(methodConfig.prompt_format); | |
| updateExampleQuestion(methodConfig.example_question); | |
| } catch (error) { | |
| console.error('Failed to load configuration:', error); | |
| showError('Failed to load configuration. Please refresh the page.'); | |
| } | |
| } | |
| // Update model list based on selected provider | |
| function updateModelList() { | |
| const provider = document.getElementById('api-provider').value; | |
| const modelSelect = document.getElementById('model'); | |
| modelSelect.innerHTML = ''; // Clear current options | |
| const models = currentConfig.general.available_models; | |
| const providers = currentConfig.general.model_providers; | |
| models.forEach(model => { | |
| if (providers[model] === provider) { | |
| const option = document.createElement('option'); | |
| option.value = model; | |
| option.textContent = model; | |
| modelSelect.appendChild(option); | |
| } | |
| }); | |
| } | |
| // Update prompt format when method changes | |
| document.getElementById('reasoning-method').addEventListener('change', async (event) => { | |
| try { | |
| const response = await fetch(`/method-config/${event.target.value}`); | |
| const methodConfig = await response.json(); | |
| updatePromptFormat(methodConfig.prompt_format); | |
| updateExampleQuestion(methodConfig.example_question); | |
| } catch (error) { | |
| console.error('Failed to load method configuration:', error); | |
| showError('Failed to update method configuration.'); | |
| } | |
| }); | |
| function updatePromptFormat(format) { | |
| document.getElementById('prompt-format').value = format; | |
| } | |
| function updateExampleQuestion(question) { | |
| document.getElementById('question').value = question; | |
| } | |
| function adjustZoom(delta) { | |
| // Do nothing if zooming is locked | |
| if (window.isZoomLocked) return; | |
| const newZoom = Math.min(Math.max(currentZoom + delta, MIN_ZOOM), MAX_ZOOM); | |
| if (newZoom !== currentZoom) { | |
| currentZoom = newZoom; | |
| applyZoom(); | |
| } | |
| } | |
| function resetZoom() { | |
| // Do nothing if zooming is locked | |
| if (window.isZoomLocked) return; | |
| currentZoom = 1; | |
| applyZoom(); | |
| } | |
| function applyZoom() { | |
| const container = document.getElementById('mermaid-container'); | |
| container.style.transform = `scale(${currentZoom})`; | |
| // Update zoom level display | |
| const percentage = Math.round(currentZoom * 100); | |
| document.getElementById('zoom-level').textContent = `${percentage}%`; | |
| } | |
| function lockVisualization() { | |
| window.isZoomLocked = true; | |
| const zoomButtons = document.querySelectorAll('.zoom-button'); | |
| zoomButtons.forEach(button => button.disabled = true); | |
| document.querySelector('.visualization-wrapper').style.pointerEvents = 'none'; | |
| } | |
| function unlockVisualization() { | |
| window.isZoomLocked = false; | |
| const zoomButtons = document.querySelectorAll('.zoom-button'); | |
| zoomButtons.forEach(button => button.disabled = false); | |
| document.querySelector('.visualization-wrapper').style.pointerEvents = 'auto'; | |
| } | |
| async function downloadDiagram() { | |
| // Do nothing if zooming is locked | |
| if (window.isZoomLocked) return; | |
| const diagramContainer = document.getElementById('mermaid-diagram'); | |
| if (!diagramContainer || !diagramContainer.querySelector('svg')) { | |
| alert('No diagram available to download'); | |
| return; | |
| } | |
| try { | |
| // Get the SVG element | |
| const svg = diagramContainer.querySelector('svg'); | |
| // Create a copy of the SVG to modify | |
| const svgCopy = svg.cloneNode(true); | |
| // Ensure the SVG has proper dimensions | |
| const bbox = svg.getBBox(); | |
| svgCopy.setAttribute('width', bbox.width); | |
| svgCopy.setAttribute('height', bbox.height); | |
| svgCopy.setAttribute('viewBox', `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`); | |
| // Convert SVG to string | |
| const serializer = new XMLSerializer(); | |
| const svgString = serializer.serializeToString(svgCopy); | |
| // Create blob and download link | |
| const blob = new Blob([svgString], {type: 'image/svg+xml'}); | |
| const url = URL.createObjectURL(blob); | |
| // Create temporary link and trigger download | |
| const link = document.createElement('a'); | |
| link.href = url; | |
| link.download = 'reasoning_diagram.svg'; | |
| document.body.appendChild(link); | |
| link.click(); | |
| // Cleanup | |
| document.body.removeChild(link); | |
| URL.revokeObjectURL(url); | |
| } catch (error) { | |
| console.error('Error downloading diagram:', error); | |
| alert('Failed to download diagram'); | |
| } | |
| } | |
| function validateInputs() { | |
| const apiKey = document.getElementById('api-key').value.trim(); | |
| const question = document.getElementById('question').value.trim(); | |
| let isValid = true; | |
| // Validate API Key | |
| if (!apiKey) { | |
| document.getElementById('api-key-error').style.display = 'block'; | |
| isValid = false; | |
| } else { | |
| document.getElementById('api-key-error').style.display = 'none'; | |
| } | |
| // Validate Question | |
| if (!question) { | |
| document.getElementById('question-error').style.display = 'block'; | |
| isValid = false; | |
| } else { | |
| document.getElementById('question-error').style.display = 'none'; | |
| } | |
| return isValid; | |
| } | |
| function showError(message) { | |
| const rawOutput = document.getElementById('raw-output'); | |
| rawOutput.textContent = `Error: ${message}`; | |
| rawOutput.style.color = '#dc2626'; | |
| } | |
| // Process question | |
| async function processQuestion(isMetaReasoning = false) { | |
| if (!validateInputs()) { | |
| return; | |
| } | |
| // Reset Zoom before processing question | |
| resetZoom(); | |
| const processButton = document.getElementById('process-btn'); | |
| const metaButton = document.getElementById('meta-btn'); | |
| const rawOutput = document.getElementById('raw-output'); | |
| processButton.disabled = true; | |
| metaButton.disabled = true; | |
| processButton.textContent = 'Processing...'; | |
| rawOutput.textContent = 'Loading...'; | |
| rawOutput.style.color = '#1f2937'; | |
| // Lock visualization | |
| lockVisualization(); | |
| const data = { | |
| provider: document.getElementById('api-provider').value, | |
| api_key: document.getElementById('api-key').value, | |
| model: document.getElementById('model').value, | |
| max_tokens: parseInt(document.getElementById('max-tokens').value), | |
| question: document.getElementById('question').value, | |
| prompt_format: document.getElementById('prompt-format').value, | |
| reasoning_method: document.getElementById('reasoning-method').value, | |
| chars_per_line: parseInt(document.getElementById('chars-per-line').value), | |
| max_lines: parseInt(document.getElementById('max-lines').value) | |
| }; | |
| try { | |
| const response = await fetch('/process', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify(data) | |
| }); | |
| const result = await response.json(); | |
| if (result.success) { | |
| rawOutput.textContent = result.raw_output; | |
| rawOutput.style.color = '#1f2937'; | |
| if (result.visualization) { | |
| const container = document.getElementById('mermaid-diagram'); | |
| container.innerHTML = result.visualization; | |
| document.getElementById('mermaid-container').classList.add('has-visualization'); | |
| resetZoom(); | |
| mermaid.init(); | |
| } | |
| } else { | |
| showError(result.error || 'Unknown error occurred'); | |
| } | |
| } catch (error) { | |
| showError('Failed to process request: ' + error.message); | |
| } finally { | |
| // Unlock visualization | |
| unlockVisualization(); | |
| processButton.disabled = false; | |
| metaButton.disabled = false; | |
| processButton.textContent = 'Start Reasoning'; | |
| if (isMetaReasoning) { | |
| metaButton.textContent = 'Meta Reasoning'; | |
| } | |
| } | |
| } | |
| // Meta Reasoning function | |
| async function metaReasoning() { | |
| const metaButton = document.getElementById('meta-btn'); | |
| const rawOutput = document.getElementById('raw-output'); | |
| try { | |
| metaButton.disabled = true; | |
| metaButton.textContent = 'Selecting Method...'; | |
| rawOutput.textContent = 'Analyzing question to select best method...'; | |
| // Get current parameters | |
| const data = { | |
| provider: document.getElementById('api-provider').value, | |
| api_key: document.getElementById('api-key').value, | |
| model: document.getElementById('model').value, | |
| question: document.getElementById('question').value | |
| }; | |
| // Call the method selection endpoint | |
| const response = await fetch('/select-method', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify(data) | |
| }); | |
| const result = await response.json(); | |
| if (result.success) { | |
| // Set the selected method | |
| const methodSelect = document.getElementById('reasoning-method'); | |
| methodSelect.value = result.selected_method; | |
| // Fetch and update the corresponding method configuration | |
| const methodResponse = await fetch(`/method-config/${result.selected_method}`); | |
| const methodConfig = await methodResponse.json(); | |
| if (methodConfig) { | |
| // Update the prompt format | |
| updatePromptFormat(methodConfig.prompt_format); | |
| // Update example question if needed | |
| if (document.getElementById('question').value === '') { | |
| updateExampleQuestion(methodConfig.example_question); | |
| } | |
| console.log(`Selected reasoning method: ${methodConfig.name}`); | |
| // Update button to show method was selected | |
| metaButton.textContent = 'Method Selected'; | |
| // Process the question with the selected method | |
| await processQuestion(true); | |
| } else { | |
| showError('Failed to load method configuration'); | |
| metaButton.textContent = 'Meta Reasoning'; | |
| } | |
| } else { | |
| showError(result.error || 'Failed to select method'); | |
| metaButton.textContent = 'Meta Reasoning'; | |
| } | |
| } catch (error) { | |
| console.error('Meta reasoning error:', error); | |
| showError('Failed to execute meta reasoning'); | |
| metaButton.disabled = false; | |
| metaButton.textContent = 'Meta Reasoning'; | |
| } | |
| } | |
| // Add event listener for mouse wheel zoom | |
| document.querySelector('.visualization-wrapper').addEventListener('wheel', function(e) { | |
| // Do nothing if zooming is locked | |
| if (window.isZoomLocked) { | |
| e.preventDefault(); | |
| return; | |
| } | |
| if (e.ctrlKey) { | |
| e.preventDefault(); // Prevent default zoom | |
| const delta = e.deltaY > 0 ? -0.1 : 0.1; | |
| adjustZoom(delta); | |
| } | |
| }); | |
| // Load configuration when page loads | |
| document.addEventListener('DOMContentLoaded', loadConfig); | |
| </script> | |
| </body> | |
| </html> |