| <!DOCTYPE html> |
| <html lang="zh-CN"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>AI学习助手</title> |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"> |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"> |
| <style> |
| :root { |
| --primary-color: #4361ee; |
| --secondary-color: #3f37c9; |
| --accent-color: #4cc9f0; |
| --success-color: #4caf50; |
| --warning-color: #ff9800; |
| --danger-color: #f44336; |
| --light-color: #f8f9fa; |
| --dark-color: #212529; |
| --border-color: #dee2e6; |
| --border-radius: 0.375rem; |
| } |
| |
| body { |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| margin: 0; |
| padding: 0; |
| height: 100vh; |
| background-color: #f5f7fa; |
| color: #333; |
| display: flex; |
| flex-direction: column; |
| } |
| |
| .header { |
| background-color: #fff; |
| border-bottom: 1px solid var(--border-color); |
| padding: 1rem; |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); |
| } |
| |
| .header-content { |
| max-width: 1600px; |
| margin: 0 auto; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .header h1 { |
| margin: 0; |
| font-size: 1.5rem; |
| color: var(--primary-color); |
| } |
| |
| .main-container { |
| flex: 1; |
| display: flex; |
| max-width: 1600px; |
| margin: 0 auto; |
| padding: 1rem; |
| width: 100%; |
| box-sizing: border-box; |
| height: calc(100vh - 70px); |
| overflow: hidden; |
| } |
| |
| .chat-container { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| background-color: #fff; |
| border-radius: var(--border-radius); |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); |
| border: 1px solid var(--border-color); |
| overflow: hidden; |
| height: 100%; |
| max-width: 800px; |
| margin: 0 auto; |
| } |
| |
| |
| .main-container.with-plugin .chat-container { |
| margin: 0; |
| max-width: 350px; |
| } |
| |
| .chat-messages { |
| flex: 1; |
| overflow-y: auto; |
| padding: 1rem; |
| display: flex; |
| flex-direction: column; |
| } |
| |
| .message { |
| max-width: 90%; |
| margin-bottom: 1rem; |
| padding: 0.75rem 1rem; |
| border-radius: var(--border-radius); |
| position: relative; |
| } |
| |
| .user-message { |
| background-color: #e3f2fd; |
| align-self: flex-end; |
| color: #0d47a1; |
| } |
| |
| .bot-message { |
| background-color: #f5f5f5; |
| align-self: flex-start; |
| color: #333; |
| border-left: 3px solid var(--primary-color); |
| } |
| |
| .input-container { |
| padding: 1rem; |
| border-top: 1px solid var(--border-color); |
| background-color: #f9f9f9; |
| } |
| |
| .input-row { |
| display: flex; |
| gap: 0.5rem; |
| } |
| |
| .input-field { |
| flex: 1; |
| padding: 0.75rem 1rem; |
| border: 1px solid var(--border-color); |
| border-radius: var(--border-radius); |
| resize: none; |
| font-size: 0.95rem; |
| } |
| |
| .input-field:focus { |
| outline: none; |
| border-color: var(--primary-color); |
| box-shadow: 0 0 0 2px rgba(67, 97, 238, 0.1); |
| } |
| |
| .send-button { |
| padding: 0.75rem 1.5rem; |
| background-color: var(--primary-color); |
| color: white; |
| border: none; |
| border-radius: var(--border-radius); |
| cursor: pointer; |
| font-weight: 500; |
| transition: background-color 0.2s; |
| } |
| |
| .send-button:hover { |
| background-color: var(--secondary-color); |
| } |
| |
| .send-button:disabled { |
| background-color: #ccc; |
| cursor: not-allowed; |
| } |
| |
| .plugin-container { |
| display: none; |
| flex-direction: column; |
| background-color: #fff; |
| border-radius: var(--border-radius); |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); |
| border: 1px solid var(--border-color); |
| overflow: hidden; |
| height: 100%; |
| flex: 2; |
| margin-left: 1rem; |
| } |
| |
| .plugin-header { |
| padding: 0.75rem 1rem; |
| border-bottom: 1px solid var(--border-color); |
| background-color: #f9f9f9; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .plugin-title { |
| margin: 0; |
| font-size: 1rem; |
| font-weight: 500; |
| } |
| |
| .plugin-close { |
| background: none; |
| border: none; |
| cursor: pointer; |
| font-size: 1rem; |
| color: #777; |
| } |
| |
| .plugin-content { |
| flex: 1; |
| overflow-y: auto; |
| } |
| |
| |
| .code-plugin .plugin-content { |
| padding: 0; |
| } |
| |
| |
| .visualization-plugin .visualization-form { |
| margin-bottom: 1rem; |
| padding: 1rem; |
| } |
| |
| .visualization-plugin .visualization-result { |
| margin-top: 1rem; |
| text-align: center; |
| padding: 0 1rem; |
| } |
| |
| .visualization-plugin .visualization-image { |
| max-width: 100%; |
| border-radius: var(--border-radius); |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
| } |
| |
| |
| .mindmap-plugin .mindmap-form { |
| margin-bottom: 1rem; |
| padding: 1rem; |
| } |
| |
| .mindmap-plugin .mindmap-result { |
| margin-top: 1rem; |
| text-align: center; |
| padding: 0 1rem; |
| } |
| |
| .mindmap-plugin .mindmap-image { |
| max-width: 100%; |
| border-radius: var(--border-radius); |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
| } |
| |
| |
| .reference-container { |
| margin-top: 10px; |
| border-top: 1px dashed #ddd; |
| padding-top: 10px; |
| } |
| |
| .reference-toggle { |
| color: #666; |
| font-size: 0.9rem; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| } |
| |
| .reference-content { |
| display: none; |
| margin-top: 10px; |
| padding: 10px; |
| background-color: #f9f9f9; |
| border-radius: 5px; |
| font-size: 0.85rem; |
| } |
| |
| |
| pre { |
| background-color: #f8f9fa; |
| border-radius: 5px; |
| padding: 12px; |
| margin: 10px 0; |
| overflow-x: auto; |
| border: 1px solid #eee; |
| } |
| |
| code { |
| font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace; |
| font-size: 14px; |
| } |
| |
| |
| @media (max-width: 1200px) { |
| .main-container.with-plugin .chat-container { |
| max-width: 300px; |
| } |
| } |
| |
| @media (max-width: 992px) { |
| .main-container { |
| flex-direction: column; |
| } |
| |
| .main-container.with-plugin .chat-container { |
| max-width: 100%; |
| margin-bottom: 1rem; |
| height: 40vh; |
| } |
| |
| .plugin-container { |
| margin-left: 0; |
| height: calc(60vh - 32px); |
| } |
| } |
| |
| @media (max-width: 768px) { |
| .main-container { |
| padding: 0.5rem; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <header class="header"> |
| <div class="header-content"> |
| <h1>{{ agent_name }}</h1> |
| <div> |
| <span class="badge bg-primary">AI学习助手</span> |
| </div> |
| </div> |
| </header> |
| |
| <div class="main-container" id="main-container"> |
| <div class="chat-container"> |
| <div class="chat-messages" id="chat-messages"> |
| <div class="message bot-message"> |
| <p>你好!我是{{ agent_name }},有什么可以帮助你的吗?</p> |
| {% if agent_description %} |
| <p>{{ agent_description }}</p> |
| {% endif %} |
| </div> |
| </div> |
| |
| <div class="input-container"> |
| <div class="input-row"> |
| <textarea class="input-field" id="user-input" placeholder="输入您的问题..." rows="2"></textarea> |
| <button class="send-button" id="send-button"> |
| <i class="bi bi-send"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="plugin-container code-plugin" id="code-plugin"> |
| <div class="plugin-header"> |
| <h3 class="plugin-title">Python代码执行</h3> |
| <button class="plugin-close" id="close-code-plugin"> |
| <i class="bi bi-x-lg"></i> |
| </button> |
| </div> |
| <div class="plugin-content"> |
| <iframe id="code-execution-frame" src="" style="width: 100%; height: 100%; border: none;"></iframe> |
| </div> |
| </div> |
| |
| |
| <div class="plugin-container visualization-plugin" id="visualization-plugin"> |
| <div class="plugin-header"> |
| <h3 class="plugin-title">3D可视化</h3> |
| <button class="plugin-close" id="close-visualization-plugin"> |
| <i class="bi bi-x-lg"></i> |
| </button> |
| </div> |
| <div class="plugin-content"> |
| <div class="visualization-result" id="visualization-result"> |
| <div class="text-center py-4"> |
| <div class="spinner-border text-primary" role="status"> |
| <span class="visually-hidden">加载中...</span> |
| </div> |
| <p class="mt-3">正在准备3D可视化...</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="plugin-container mindmap-plugin" id="mindmap-plugin"> |
| <div class="plugin-header"> |
| <h3 class="plugin-title">思维导图</h3> |
| <button class="plugin-close" id="close-mindmap-plugin"> |
| <i class="bi bi-x-lg"></i> |
| </button> |
| </div> |
| <div class="plugin-content"> |
| <div class="mindmap-result" id="mindmap-result"> |
| <div class="text-center py-4"> |
| <div class="spinner-border text-primary" role="status"> |
| <span class="visually-hidden">加载中...</span> |
| </div> |
| <p class="mt-3">正在生成思维导图...</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <script> |
| |
| const agentId = "{{ agent_id }}"; |
| const token = "{{ token }}"; |
| let executionContext = null; |
| const mainContainer = document.getElementById('main-container'); |
| |
| |
| const chatMessages = document.getElementById('chat-messages'); |
| const userInput = document.getElementById('user-input'); |
| const sendButton = document.getElementById('send-button'); |
| |
| |
| const codePlugin = document.getElementById('code-plugin'); |
| const closeCodePlugin = document.getElementById('close-code-plugin'); |
| const codeExecutionFrame = document.getElementById('code-execution-frame'); |
| |
| |
| const visualizationPlugin = document.getElementById('visualization-plugin'); |
| const closeVisualizationPlugin = document.getElementById('close-visualization-plugin'); |
| const visualizationResult = document.getElementById('visualization-result'); |
| |
| |
| const mindmapPlugin = document.getElementById('mindmap-plugin'); |
| const closeMindmapPlugin = document.getElementById('close-mindmap-plugin'); |
| const mindmapResult = document.getElementById('mindmap-result'); |
| |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| sendButton.addEventListener('click', sendMessage); |
| |
| |
| userInput.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter' && !e.shiftKey) { |
| e.preventDefault(); |
| sendMessage(); |
| } |
| }); |
| |
| |
| closeCodePlugin.addEventListener('click', () => { |
| codePlugin.style.display = 'none'; |
| |
| codeExecutionFrame.src = ''; |
| updateMainContainerLayout(); |
| }); |
| |
| closeVisualizationPlugin.addEventListener('click', () => { |
| visualizationPlugin.style.display = 'none'; |
| updateMainContainerLayout(); |
| }); |
| |
| closeMindmapPlugin.addEventListener('click', () => { |
| mindmapPlugin.style.display = 'none'; |
| updateMainContainerLayout(); |
| }); |
| }); |
| |
| |
| function updateMainContainerLayout() { |
| |
| const isAnyPluginVisible = |
| codePlugin.style.display === 'flex' || |
| visualizationPlugin.style.display === 'flex' || |
| mindmapPlugin.style.display === 'flex'; |
| |
| |
| if (isAnyPluginVisible) { |
| mainContainer.classList.add('with-plugin'); |
| } else { |
| mainContainer.classList.remove('with-plugin'); |
| } |
| } |
| |
| |
| async function sendMessage() { |
| const message = userInput.value.trim(); |
| if (!message) return; |
| |
| |
| addMessage(message, true); |
| |
| |
| userInput.value = ''; |
| |
| |
| sendButton.disabled = true; |
| |
| try { |
| |
| const response = await fetch(`/api/student/chat/${agentId}`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json' |
| }, |
| body: JSON.stringify({ |
| message: message, |
| token: token |
| }) |
| }); |
| |
| const data = await response.json(); |
| |
| if (data.success) { |
| |
| const processedContent = processResponseContent(data.message); |
| |
| |
| const messageElement = addMessage(processedContent, false); |
| |
| |
| if (data.references && data.references.length > 0) { |
| addReferences(messageElement, data.references); |
| } |
| |
| |
| if (data.tools && data.tools.length > 0) { |
| |
| hideAllPlugins(); |
| |
| |
| activatePlugins(data.message, data.tools); |
| } |
| } else { |
| |
| addMessage(`错误: ${data.message}`, false); |
| } |
| } catch (error) { |
| console.error('发送请求出错:', error); |
| addMessage('发送请求时出错,请重试', false); |
| } finally { |
| |
| sendButton.disabled = false; |
| } |
| } |
| |
| |
| function processResponseContent(content) { |
| |
| return content.replace(/===参考来源开始===[\s\S]*?===参考来源结束===/, ''); |
| } |
| |
| |
| function addReferences(messageElement, references) { |
| |
| const referenceContainer = document.createElement('div'); |
| referenceContainer.className = 'reference-container'; |
| |
| |
| const toggleButton = document.createElement('div'); |
| toggleButton.className = 'reference-toggle'; |
| toggleButton.textContent = '参考来源'; |
| toggleButton.style.color = '#666'; |
| toggleButton.style.fontSize = '0.9rem'; |
| toggleButton.style.cursor = 'pointer'; |
| toggleButton.style.display = 'flex'; |
| toggleButton.style.alignItems = 'center'; |
| |
| |
| const referenceContent = document.createElement('div'); |
| referenceContent.className = 'reference-content'; |
| |
| |
| references.forEach(ref => { |
| const refItem = document.createElement('div'); |
| refItem.style.marginBottom = '8px'; |
| refItem.style.paddingBottom = '8px'; |
| refItem.style.borderBottom = '1px solid #eee'; |
| |
| refItem.innerHTML = ` |
| <div><strong>[${ref.index}]</strong> ${ref.summary}</div> |
| <div><small>来源: ${ref.file_name}</small></div> |
| `; |
| |
| referenceContent.appendChild(refItem); |
| }); |
| |
| |
| toggleButton.addEventListener('click', () => { |
| if (referenceContent.style.display === 'none') { |
| referenceContent.style.display = 'block'; |
| toggleButton.textContent = '折叠参考来源'; |
| } else { |
| referenceContent.style.display = 'none'; |
| toggleButton.textContent = '参考来源'; |
| } |
| }); |
| |
| |
| referenceContainer.appendChild(toggleButton); |
| referenceContainer.appendChild(referenceContent); |
| messageElement.appendChild(referenceContainer); |
| } |
| |
| |
| function addMessage(content, isUser) { |
| const messageDiv = document.createElement('div'); |
| messageDiv.className = isUser ? 'message user-message' : 'message bot-message'; |
| |
| |
| const processedContent = processMessageContent(content); |
| messageDiv.innerHTML = processedContent; |
| |
| chatMessages.appendChild(messageDiv); |
| |
| |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| |
| |
| if (!isUser) { |
| applySyntaxHighlighting(messageDiv); |
| } |
| |
| return messageDiv; |
| } |
| |
| |
| function processMessageContent(content) { |
| |
| const codeBlocks = []; |
| let processedContent = content.replace(/```(\w*)\n([\s\S]*?)\n```/g, function(match, language, code) { |
| const id = codeBlocks.length; |
| codeBlocks.push({ language, code }); |
| return `__CODE_BLOCK_${id}__`; |
| }); |
| |
| |
| processedContent = processedContent.replace(/\n/g, '<br>'); |
| |
| |
| processedContent = processedContent.replace(/__CODE_BLOCK_(\d+)__/g, function(match, id) { |
| const { language, code } = codeBlocks[id]; |
| const langClass = language ? ` class="language-${language}"` : ''; |
| return `<pre><code${langClass}>${escapeHtml(code)}</code></pre>`; |
| }); |
| |
| return processedContent; |
| } |
| |
| |
| function applySyntaxHighlighting(element) { |
| |
| if (typeof hljs !== 'undefined') { |
| |
| element.querySelectorAll('pre code').forEach((block) => { |
| hljs.highlightBlock(block); |
| }); |
| } |
| } |
| |
| |
| function escapeHtml(text) { |
| return text |
| .replace(/&/g, "&") |
| .replace(/</g, "<") |
| .replace(/>/g, ">") |
| .replace(/"/g, """) |
| .replace(/'/g, "'"); |
| } |
| |
| |
| function hideAllPlugins() { |
| codePlugin.style.display = 'none'; |
| visualizationPlugin.style.display = 'none'; |
| mindmapPlugin.style.display = 'none'; |
| updateMainContainerLayout(); |
| } |
| |
| |
| function extractCodeBlocks(message) { |
| const codeBlocks = []; |
| const codeRegex = /```python\n([\s\S]*?)\n```/g; |
| |
| let match; |
| while ((match = codeRegex.exec(message)) !== null) { |
| codeBlocks.push(match[1]); |
| } |
| |
| return codeBlocks; |
| } |
| |
| |
| function extract3DVisualizationCode(message) { |
| |
| const codeRegex = /```python\s*(import[\s\S]*?def create_3d_plot\(\):[\s\S]*?return[\s\S]*?})\s*```/i; |
| const match = codeRegex.exec(message); |
| |
| if (match) { |
| return match[1].trim(); |
| } |
| |
| return null; |
| } |
| |
| |
| function extractMindmapContent(message) { |
| |
| const mindmapRegex = /@startmindmap\n([\s\S]*?)@endmindmap/; |
| const match = mindmapRegex.exec(message); |
| |
| if (match) { |
| return `@startmindmap\n${match[1]}\n@endmindmap`; |
| } |
| |
| return null; |
| } |
| |
| |
| function activateCodePlugin(message) { |
| |
| const codeBlocks = extractCodeBlocks(message); |
| |
| if (codeBlocks.length > 0) { |
| const isAlreadyVisible = codePlugin.style.display === 'flex'; |
| |
| |
| codePlugin.style.display = 'flex'; |
| updateMainContainerLayout(); |
| |
| |
| const iframe = document.getElementById('code-execution-frame'); |
| |
| |
| if (isAlreadyVisible && iframe.contentWindow) { |
| |
| iframe.contentWindow.postMessage({ |
| type: 'setCode', |
| code: codeBlocks[0] |
| }, '*'); |
| } else { |
| |
| let src = '/code_execution.html'; |
| if (codeBlocks.length > 0) { |
| src += `?code=${encodeURIComponent(codeBlocks[0])}`; |
| } |
| |
| iframe.src = src; |
| |
| |
| iframe.onload = function() { |
| if (codeBlocks.length > 0) { |
| iframe.contentWindow.postMessage({ |
| type: 'setCode', |
| code: codeBlocks[0] |
| }, '*'); |
| } |
| }; |
| } |
| } |
| } |
| |
| |
| |
| function activate3DVisualization(message) { |
| const code = extract3DVisualizationCode(message); |
| |
| if (code) { |
| |
| visualizationPlugin.style.display = 'flex'; |
| updateMainContainerLayout(); |
| |
| |
| visualizationResult.innerHTML = ` |
| <div class="text-center py-4"> |
| <div class="spinner-border text-primary" role="status"> |
| <span class="visually-hidden">生成中...</span> |
| </div> |
| <p class="mt-3">正在生成3D图形,请稍候...</p> |
| </div> |
| `; |
| |
| |
| fetch('/api/visualization/3d-surface', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json' |
| }, |
| body: JSON.stringify({ code: code }) |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| |
| visualizationResult.innerHTML = ` |
| <div class="visualization-iframe-container" style="width:100%; height:500px;"> |
| <iframe src="${data.html_url}" style="width:100%; height:100%; border:none;"></iframe> |
| </div> |
| <div class="text-center mt-3"> |
| <p>3D图形生成成功</p> |
| <a href="${data.html_url}" class="btn btn-sm btn-outline-primary" target="_blank"> |
| <i class="bi bi-arrows-fullscreen"></i> 全屏查看 |
| </a> |
| </div> |
| `; |
| } else { |
| visualizationResult.innerHTML = ` |
| <div class="alert alert-danger"> |
| <i class="bi bi-exclamation-triangle-fill me-2"></i> |
| 生成失败: ${data.message} |
| </div> |
| `; |
| } |
| }) |
| .catch(error => { |
| console.error('生成3D图形出错:', error); |
| visualizationResult.innerHTML = ` |
| <div class="alert alert-danger"> |
| <i class="bi bi-exclamation-triangle-fill me-2"></i> |
| 生成3D图形时发生错误,请重试 |
| </div> |
| `; |
| }); |
| } |
| } |
| |
| function activateMindmap(message) { |
| const content = extractMindmapContent(message); |
| |
| if (content) { |
| |
| mindmapPlugin.style.display = 'flex'; |
| updateMainContainerLayout(); |
| |
| |
| mindmapResult.innerHTML = ` |
| <div class="text-center py-4"> |
| <div class="spinner-border text-primary" role="status"> |
| <span class="visually-hidden">生成中...</span> |
| </div> |
| <p class="mt-3">正在生成思维导图,请稍候...</p> |
| </div> |
| `; |
| |
| |
| fetch('/api/visualization/mindmap', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json' |
| }, |
| body: JSON.stringify({ content: content }) |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| mindmapResult.innerHTML = ` |
| <div class="text-center"> |
| <img src="${data.url}" class="mindmap-image" alt="思维导图"> |
| <p class="mt-3">生成成功!</p> |
| </div> |
| `; |
| } |
| else { |
| mindmapResult.innerHTML = ` |
| <div class="alert alert-danger"> |
| <i class="bi bi-exclamation-triangle-fill me-2"></i> |
| 生成失败: ${data.message} |
| </div> |
| `; |
| } |
| }) |
| .catch(error => { |
| console.error('生成思维导图出错:', error); |
| mindmapResult.innerHTML = ` |
| <div class="alert alert-danger"> |
| <i class="bi bi-exclamation-triangle-fill me-2"></i> |
| 生成思维导图时发生错误,请重试 |
| </div> |
| `; |
| }); |
| } |
| } |
| |
| |
| function activatePlugins(message, tools) { |
| |
| if (tools.includes('code') && message.includes('```python')) { |
| activateCodePlugin(message); |
| } |
| |
| |
| if (tools.includes('visualization') && |
| (message.includes('def create_3d_plot') || |
| message.includes('3D') || message.includes('可视化'))) { |
| activate3DVisualization(message); |
| } |
| |
| |
| if (tools.includes('mindmap') && |
| (message.includes('@startmindmap') || |
| message.includes('思维导图'))) { |
| activateMindmap(message); |
| } |
| } |
| </script> |
| |
| |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/python.min.js"></script> |
| <script> |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| if (typeof hljs !== 'undefined') { |
| hljs.configure({ |
| languages: ['python'] |
| }); |
| } |
| }); |
| </script> |
| </body> |
| </html> |