<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>UniXcoder Demo - IFS Code Search</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
font-weight: 300;
}
.header p {
font-size: 1.2rem;
opacity: 0.9;
margin-bottom: 20px;
}
.demo-badges {
display: flex;
justify-content: center;
gap: 15px;
flex-wrap: wrap;
}
.badge {
background: rgba(255, 255, 255, 0.2);
padding: 8px 16px;
border-radius: 20px;
font-size: 0.9rem;
border: 1px solid rgba(255, 255, 255, 0.3);
}
.search-section {
padding: 40px;
background: white;
}
.search-box {
margin-bottom: 30px;
}
.search-input-container {
position: relative;
margin-bottom: 20px;
}
.search-input {
width: 100%;
padding: 20px 60px 20px 20px;
font-size: 1.2rem;
border: 2px solid #e0e6ed;
border-radius: 15px;
outline: none;
transition: all 0.3s ease;
}
.search-input:focus {
border-color: #667eea;
box-shadow: 0 0 20px rgba(102, 126, 234, 0.2);
}
.search-button {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: #667eea;
color: white;
border: none;
padding: 12px 20px;
border-radius: 10px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
}
.search-button:hover {
background: #5a6fd8;
}
.search-button:disabled {
background: #ccc;
cursor: not-allowed;
}
.search-options {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.option-group {
display: flex;
flex-direction: column;
}
.option-group label {
font-weight: 600;
margin-bottom: 8px;
color: #2c3e50;
}
.option-group select,
.option-group input {
padding: 12px;
border: 2px solid #e0e6ed;
border-radius: 8px;
font-size: 1rem;
}
.status-section {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-bottom: 30px;
border-left: 4px solid #667eea;
}
.status-loading {
border-left-color: #ffc107;
}
.status-error {
border-left-color: #dc3545;
}
.status-ready {
border-left-color: #28a745;
}
.results-section {
margin-top: 30px;
}
.results-header {
display: flex;
justify-content: between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #e0e6ed;
}
.results-stats {
font-size: 0.9rem;
color: #666;
}
.result-card {
background: white;
border: 1px solid #e0e6ed;
border-radius: 15px;
margin-bottom: 20px;
padding: 25px;
transition: all 0.3s ease;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.result-card:hover {
border-color: #667eea;
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.15);
transform: translateY(-2px);
}
.result-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 15px;
}
.result-title {
font-size: 1.3rem;
font-weight: 600;
color: #2c3e50;
margin-bottom: 5px;
}
.result-meta {
display: flex;
gap: 15px;
flex-wrap: wrap;
margin-bottom: 15px;
}
.meta-item {
background: #f8f9fa;
padding: 4px 12px;
border-radius: 15px;
font-size: 0.9rem;
color: #666;
border: 1px solid #e0e6ed;
}
.score-badge {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
font-weight: 600;
border: none;
}
.code-snippet {
background: #f8f9fa;
border: 1px solid #e0e6ed;
border-radius: 10px;
padding: 20px;
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
font-size: 0.9rem;
line-height: 1.5;
overflow-x: auto;
margin: 15px 0;
white-space: pre;
}
.result-explanation {
background: #e8f4f8;
border-left: 4px solid #667eea;
padding: 15px;
border-radius: 0 10px 10px 0;
font-style: italic;
color: #2c3e50;
}
.loading {
text-align: center;
padding: 40px;
color: #667eea;
}
.loading::after {
content: "";
width: 40px;
height: 40px;
border: 4px solid #e0e6ed;
border-top: 4px solid #667eea;
border-radius: 50%;
display: inline-block;
animation: spin 1s linear infinite;
margin-left: 10px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.no-results {
text-align: center;
padding: 60px;
color: #666;
}
.no-results h3 {
margin-bottom: 10px;
color: #2c3e50;
}
.comparison-link {
position: fixed;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.9);
padding: 12px 20px;
border-radius: 25px;
text-decoration: none;
color: #2c3e50;
font-weight: 600;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.comparison-link:hover {
background: white;
transform: translateY(-2px);
}
@media (max-width: 768px) {
.container {
margin: 10px;
border-radius: 10px;
}
.header {
padding: 20px;
}
.header h1 {
font-size: 2rem;
}
.search-section {
padding: 20px;
}
.search-options {
grid-template-columns: 1fr;
}
.comparison-link {
position: static;
display: block;
margin: 20px auto;
text-align: center;
max-width: 200px;
}
}
</style>
</head>
<body>
<a href="/" class="comparison-link">β Original Search</a>
<div class="container">
<div class="header">
<h1>π― UniXcoder Demo</h1>
<p>Zero-shot semantic code search for IFS modules</p>
<div class="demo-badges">
<div class="badge">Microsoft UniXcoder</div>
<div class="badge">FAISS Vector Search</div>
<div class="badge">No Training Required</div>
<div class="badge">proj β’ prjrep β’ fndbas β’ accrul</div>
</div>
</div>
<div class="search-section">
<!-- Status Section -->
<div id="status-section" class="status-section status-loading">
<div id="status-content">
<strong>π§ Initializing...</strong> Starting UniXcoder engine...
</div>
</div>
<!-- Search Box -->
<div class="search-box">
<div class="search-input-container">
<input
type="text"
id="search-input"
class="search-input"
placeholder="Search for code... (e.g., 'create project', 'validate accounting rules', 'error handling')"
disabled
/>
<button id="search-button" class="search-button" disabled>
Search
</button>
</div>
<div class="search-options">
<div class="option-group">
<label for="module-filter">Module Filter</label>
<select id="module-filter">
<option value="">All modules</option>
<option value="proj">proj (Project Management)</option>
<option value="prjrep">prjrep (Project Reporting)</option>
<option value="fndbas">fndbas (Foundation Basics)</option>
<option value="accrul">accrul (Accounting Rules)</option>
</select>
</div>
<div class="option-group">
<label for="max-results">Max Results</label>
<select id="max-results">
<option value="5">5 results</option>
<option value="10" selected>10 results</option>
<option value="20">20 results</option>
</select>
</div>
<div class="option-group">
<label for="min-score">Min Similarity</label>
<input
type="range"
id="min-score"
min="0.1"
max="0.8"
step="0.1"
value="0.2"
/>
<span id="min-score-value">0.2</span>
</div>
</div>
</div>
<!-- Results Section -->
<div id="results-section" class="results-section" style="display: none">
<div class="results-header">
<h2>Search Results</h2>
<div id="results-stats" class="results-stats"></div>
</div>
<div id="results-container"></div>
</div>
</div>
</div>
<script>
// Application state
let demoStatus = "loading";
let searchInProgress = false;
// DOM elements
const statusSection = document.getElementById("status-section");
const statusContent = document.getElementById("status-content");
const searchInput = document.getElementById("search-input");
const searchButton = document.getElementById("search-button");
const moduleFilter = document.getElementById("module-filter");
const maxResults = document.getElementById("max-results");
const minScore = document.getElementById("min-score");
const minScoreValue = document.getElementById("min-score-value");
const resultsSection = document.getElementById("results-section");
const resultsStats = document.getElementById("results-stats");
const resultsContainer = document.getElementById("results-container");
// Update min score display
minScore.addEventListener("input", () => {
minScoreValue.textContent = minScore.value;
});
// Check demo status
async function checkDemoStatus() {
try {
const response = await fetch("/api/demo/status");
const status = await response.json();
updateStatusUI(status);
if (status.available) {
demoStatus = "ready";
enableSearch();
} else if (status.status === "error") {
demoStatus = "error";
} else {
// Still loading, check again in 3 seconds
setTimeout(checkDemoStatus, 3000);
}
} catch (error) {
console.error("Error checking demo status:", error);
updateStatusUI({
status: "error",
error: error.message,
available: false,
});
demoStatus = "error";
}
}
function updateStatusUI(status) {
const section = statusSection;
section.className = "status-section";
if (status.available) {
section.classList.add("status-ready");
statusContent.innerHTML = `
<strong>β
Demo Ready!</strong>
Indexed ${
status.total_chunks || 0
} code chunks from modules: ${(status.modules || []).join(
", "
)}
<br><small>Model: ${
status.model || "UniXcoder"
} | Engine: ${status.search_engine || "FAISS"}</small>
`;
} else if (status.status === "error") {
section.classList.add("status-error");
statusContent.innerHTML = `
<strong>β Demo Unavailable</strong>
${status.error || "Unknown error"}
<br><small>Try refreshing the page or check the server logs</small>
`;
} else {
section.classList.add("status-loading");
statusContent.innerHTML = `
<strong>π§ Initializing Demo...</strong>
Loading UniXcoder model and building search index...
<br><small>This may take a few minutes on first run</small>
`;
}
}
function enableSearch() {
searchInput.disabled = false;
searchButton.disabled = false;
searchInput.focus();
}
// Search functionality
async function performSearch() {
if (searchInProgress || demoStatus !== "ready") return;
const query = searchInput.value.trim();
if (!query) return;
searchInProgress = true;
searchButton.disabled = true;
searchButton.textContent = "Searching...";
try {
const params = new URLSearchParams({
query: query,
limit: maxResults.value,
min_score: minScore.value,
});
if (moduleFilter.value) {
params.append("module", moduleFilter.value);
}
const response = await fetch(`/api/demo/search?${params}`);
const data = await response.json();
displayResults(data);
} catch (error) {
console.error("Search error:", error);
displayError("Search failed: " + error.message);
} finally {
searchInProgress = false;
searchButton.disabled = false;
searchButton.textContent = "Search";
}
}
function displayResults(data) {
resultsSection.style.display = "block";
// Update stats
resultsStats.innerHTML = `
Found ${data.total_results} results in ${data.search_time_ms}ms
using ${data.search_method}
`;
// Clear and populate results
resultsContainer.innerHTML = "";
if (data.results && data.results.length > 0) {
data.results.forEach((result) => {
const resultCard = createResultCard(result);
resultsContainer.appendChild(resultCard);
});
} else {
resultsContainer.innerHTML = `
<div class="no-results">
<h3>No results found</h3>
<p>Try adjusting your search terms or filters</p>
</div>
`;
}
}
function createResultCard(result) {
const card = document.createElement("div");
card.className = "result-card";
card.innerHTML = `
<div class="result-header">
<div>
<div class="result-title">${escapeHtml(
result.title
)}</div>
<div class="result-meta">
<span class="meta-item score-badge">Score: ${
result.similarity_score
}</span>
<span class="meta-item">Rank: ${result.rank}</span>
${
result.module
? `<span class="meta-item">Module: ${result.module.toUpperCase()}</span>`
: ""
}
${
result.language
? `<span class="meta-item">Language: ${result.language}</span>`
: ""
}
${
result.function_name
? `<span class="meta-item">Function: ${result.function_name}</span>`
: ""
}
</div>
</div>
</div>
<div class="code-snippet">${escapeHtml(result.snippet)}</div>
<div class="result-explanation">
π‘ ${escapeHtml(result.relevance_explanation)}
</div>
<div style="margin-top: 15px; font-size: 0.9rem; color: #666;">
π ${escapeHtml(result.file_name)}
${
result.line_start
? `(lines ${result.line_start}-${result.line_end})`
: ""
}
${result.has_error_handling ? " π‘οΈ Has error handling" : ""}
${
result.complexity_score
? ` π Complexity: ${result.complexity_score}`
: ""
}
</div>
`;
return card;
}
function displayError(message) {
resultsSection.style.display = "block";
resultsContainer.innerHTML = `
<div class="no-results">
<h3>Search Error</h3>
<p>${escapeHtml(message)}</p>
</div>
`;
}
function escapeHtml(unsafe) {
if (typeof unsafe !== "string") return "";
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// Event listeners
searchButton.addEventListener("click", performSearch);
searchInput.addEventListener("keypress", (e) => {
if (e.key === "Enter") {
performSearch();
}
});
// Sample queries for demonstration
const sampleQueries = [
"create project",
"generate project report",
"validate accounting rules",
"error handling procedures",
"calculate project costs",
"database connection setup",
"user authentication",
"API endpoint for project data",
];
// Add sample query buttons (optional)
function addSampleQueries() {
if (demoStatus === "ready") {
const sampleContainer = document.createElement("div");
sampleContainer.style.marginTop = "20px";
sampleContainer.innerHTML = `
<p style="margin-bottom: 10px; color: #666; font-size: 0.9rem;">
π‘ Try these sample queries:
</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
${sampleQueries
.map(
(query) =>
`<button onclick="searchInput.value='${query}'; performSearch();"
style="padding: 8px 15px; background: #f8f9fa; border: 1px solid #e0e6ed; border-radius: 15px; cursor: pointer; font-size: 0.9rem;">
${query}
</button>`
)
.join("")}
</div>
`;
document.querySelector(".search-box").appendChild(sampleContainer);
}
}
// Initialize the app
checkDemoStatus();
// Add sample queries after a short delay
setTimeout(addSampleQueries, 5000);
</script>
</body>
</html>