Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Citation Rendering Test</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| max-width: 800px; | |
| margin: 20px auto; | |
| padding: 20px; | |
| background-color: #f5f5f5; | |
| } | |
| .test-case { | |
| background: white; | |
| padding: 20px; | |
| margin-bottom: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| h2 { | |
| color: #333; | |
| margin-top: 0; | |
| } | |
| .result { | |
| padding: 15px; | |
| background: #f8f9fa; | |
| border-left: 4px solid #007bff; | |
| margin-top: 10px; | |
| } | |
| .inline-source-citation { | |
| color: #667eea; | |
| background: #eef2ff; | |
| padding: 0.125rem 0.375rem; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| font-weight: 500; | |
| border: 1px solid #c7d2fe; | |
| display: inline-block; | |
| text-decoration: none; | |
| margin: 0 0.125rem; | |
| } | |
| .inline-source-citation:hover { | |
| background: #ddd6fe; | |
| border-color: #a78bfa; | |
| color: #7c3aed; | |
| } | |
| .error { | |
| color: #dc3545; | |
| background: #f8d7da; | |
| padding: 10px; | |
| border-radius: 4px; | |
| margin-top: 10px; | |
| } | |
| .success { | |
| color: #28a745; | |
| background: #d4edda; | |
| padding: 10px; | |
| border-radius: 4px; | |
| margin-top: 10px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Source Citation Rendering Test</h1> | |
| <div class="test-case"> | |
| <h2>Test 1: Placeholder Generation</h2> | |
| <div id="test1-input"> | |
| Input: "Based on the PTO Policy [Source: pto_policy.md], you can take time off." | |
| </div> | |
| <div class="result" id="test1-result"></div> | |
| </div> | |
| <div class="test-case"> | |
| <h2>Test 2: Multiple Citations</h2> | |
| <div id="test2-input"> | |
| Input: "According to [Source: pto_policy.md] and [Source: remote_work_policy.md], employees have flexibility." | |
| </div> | |
| <div class="result" id="test2-result"></div> | |
| </div> | |
| <div class="test-case"> | |
| <h2>Test 3: HTML Escaping</h2> | |
| <div id="test3-input"> | |
| Input: "The policy [Source: <script>alert('xss')</script>.md] is secure." | |
| </div> | |
| <div class="result" id="test3-result"></div> | |
| </div> | |
| <script> | |
| // Simplified version of the citation functions for testing | |
| class CitationRenderer { | |
| constructor() { | |
| this.citationPlaceholders = []; | |
| } | |
| escapeHtml(text) { | |
| const div = document.createElement('div'); | |
| div.textContent = text; | |
| return div.innerHTML; | |
| } | |
| prepareInlineCitations(text, sources) { | |
| const citationPattern = /\[Source:\s*([^\]]+)\]/g; | |
| const sourceMap = new Map(); | |
| sources.forEach(source => { | |
| const filename = source.filename || source.document || source.title; | |
| if (filename) { | |
| sourceMap.set(filename.trim(), source.id || source.filename || source.document); | |
| } | |
| }); | |
| this.citationPlaceholders = []; | |
| const processedText = text.replace(citationPattern, (match, filename) => { | |
| const trimmedFilename = filename.trim(); | |
| const sourceId = sourceMap.get(trimmedFilename) || trimmedFilename; | |
| const placeholderIndex = this.citationPlaceholders.length; | |
| this.citationPlaceholders.push({ | |
| sourceId: sourceId, | |
| sourceName: trimmedFilename | |
| }); | |
| return `__CITATION_${placeholderIndex}__`; | |
| }); | |
| return processedText; | |
| } | |
| replaceCitationPlaceholders(html) { | |
| if (!this.citationPlaceholders || this.citationPlaceholders.length === 0) { | |
| return html; | |
| } | |
| let result = html; | |
| this.citationPlaceholders.forEach((citation, index) => { | |
| const placeholder = `__CITATION_${index}__`; | |
| const escapedSourceId = this.escapeHtml(citation.sourceId); | |
| const escapedSourceName = this.escapeHtml(citation.sourceName); | |
| const citationHtml = `<span class="inline-source-citation" role="button" tabindex="0" data-source-id="${escapedSourceId}" data-source-name="${escapedSourceName}" title="Click to view source: ${escapedSourceName}" aria-label="View source: ${escapedSourceName}">[Source: ${escapedSourceName}]</span>`; | |
| result = result.replace(placeholder, citationHtml); | |
| }); | |
| return result; | |
| } | |
| // Simplified markdown formatter (just escapes HTML) | |
| formatMarkdown(text) { | |
| return this.escapeHtml(text); | |
| } | |
| renderMessage(text, sources) { | |
| const textWithPlaceholders = this.prepareInlineCitations(text, sources); | |
| const escapedMarkdown = this.formatMarkdown(textWithPlaceholders); | |
| return this.replaceCitationPlaceholders(escapedMarkdown); | |
| } | |
| } | |
| // Run tests | |
| const renderer = new CitationRenderer(); | |
| // Test 1 | |
| const sources1 = [ | |
| { filename: 'pto_policy.md', id: 'pto_policy.md' } | |
| ]; | |
| const result1 = renderer.renderMessage( | |
| "Based on the PTO Policy [Source: pto_policy.md], you can take time off.", | |
| sources1 | |
| ); | |
| document.getElementById('test1-result').innerHTML = result1; | |
| if (result1.includes('<span class="inline-source-citation"')) { | |
| document.getElementById('test1-result').insertAdjacentHTML('beforeend', | |
| '<div class="success">✓ Citation rendered correctly as HTML</div>'); | |
| } else { | |
| document.getElementById('test1-result').insertAdjacentHTML('beforeend', | |
| '<div class="error">✗ Citation not rendered as HTML</div>'); | |
| } | |
| // Test 2 | |
| const sources2 = [ | |
| { filename: 'pto_policy.md', id: 'pto_policy.md' }, | |
| { filename: 'remote_work_policy.md', id: 'remote_work_policy.md' } | |
| ]; | |
| const result2 = renderer.renderMessage( | |
| "According to [Source: pto_policy.md] and [Source: remote_work_policy.md], employees have flexibility.", | |
| sources2 | |
| ); | |
| document.getElementById('test2-result').innerHTML = result2; | |
| const citationCount = (result2.match(/inline-source-citation/g) || []).length; | |
| if (citationCount === 2) { | |
| document.getElementById('test2-result').insertAdjacentHTML('beforeend', | |
| '<div class="success">✓ Multiple citations rendered correctly</div>'); | |
| } else { | |
| document.getElementById('test2-result').insertAdjacentHTML('beforeend', | |
| `<div class="error">✗ Expected 2 citations, found ${citationCount}</div>`); | |
| } | |
| // Test 3 - XSS prevention | |
| const sources3 = []; | |
| const result3 = renderer.renderMessage( | |
| "The policy [Source: <script>alert('xss')</script>.md] is secure.", | |
| sources3 | |
| ); | |
| document.getElementById('test3-result').innerHTML = result3; | |
| if (!result3.includes('<script> | |
| </script> | |
| </body> | |
| </html> | |