Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Architecture Nexus</title> | |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css"> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap'); | |
:root { | |
--bg-color: #f0f4f8; | |
--text-color: #333; | |
--card-bg: #fff; | |
--card-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
--title-gradient: linear-gradient(45deg, #2862bf, #3fe1ab); | |
--subtitle-color: #4b5563; | |
--button-gradient: linear-gradient(45deg, #3b82f6, #10b981); | |
--button-text: white; | |
--border-color: #dee2e6; | |
} | |
[data-theme="dark"] { | |
--bg-color: #1a202c; | |
--text-color: #f8f8f9; | |
--card-bg: #65738a; | |
--card-shadow: 0 4px 6px rgba(56, 54, 54, 0.3); | |
--title-gradient: linear-gradient(45deg, #4299e1, #48bb78); | |
--subtitle-color: #a0aec0; | |
--button-gradient: linear-gradient(45deg, #4299e1, #48bb78); | |
--button-text: #1a202c; | |
--border-color: #4a5568; | |
} | |
body { | |
font-family: 'Poppins', sans-serif; | |
background-color: var(--bg-color); | |
color: var(--text-color); | |
margin: 0; | |
padding: 0; | |
transition: background-color 0.3s, color 0.3s; | |
} | |
.title { | |
font-weight: 700; | |
font-size: 60px ; | |
background: var(--title-gradient); | |
-webkit-background-clip: text; | |
background-clip: text; | |
color: transparent; | |
text-align: center; | |
margin: 0; | |
padding: 2rem 0 0.5rem; | |
} | |
.subtitle { | |
font-size: 24px; | |
color: var(--subtitle-color); | |
text-align: center; | |
} | |
.generate-button { | |
background: var(--button-gradient); | |
border: none; | |
color: var(--button-text); | |
font-weight: 600; | |
} | |
.generate-button:hover { | |
opacity: 0.9; | |
} | |
.card { | |
box-shadow: var(--card-shadow); | |
background-color: var(--card-bg); | |
border-color: var(--border-color); | |
} | |
#mermaid-output { | |
width: 100%; | |
height: 100%; | |
position: relative; | |
background-color: var(--card-bg); | |
} | |
.loading-overlay { | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background-color: rgba(255, 255, 255, 0.8); | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
z-index: 1000; | |
} | |
.chart-output { | |
height: 800px; | |
width: 100%; | |
overflow: hidden; | |
position: relative; | |
} | |
#mermaid-output svg { width: 100%; height: 100%; } | |
@media (max-width: 768px) { | |
.input-group { flex-direction: column; } | |
.input-group > * { margin-bottom: 0.5rem; width: 100%; } | |
} | |
.fullscreen { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100vw; | |
height: 100vh; | |
z-index: 9999; | |
background-color: var(--card-bg); | |
} | |
.fullscreen #mermaid-output { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
overflow: auto; | |
} | |
.form-control { | |
font-size: 1.2rem; | |
background-color: var(--card-bg); | |
color: var(--text-color); | |
min-width: 600px; | |
border-color: var(--border-color); | |
} | |
.chart-controls { | |
position: absolute; | |
top: 10px; | |
right: 10px; | |
z-index: 1001; | |
} | |
.fullscreen .chart-output { | |
height: 100vh; | |
} | |
.theme-toggle { | |
position: fixed; | |
top: 10px; | |
right: 10px; | |
z-index: 1002; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="app" class="container"> | |
<button @click="toggleTheme" class="btn btn-secondary theme-toggle"> | |
<i :class="isDarkMode ? 'bi-sun' : 'bi-moon'"></i> | |
</button> | |
<h1 class="title">AI Graph Intelligence</h1> | |
<h2 class="subtitle">by Elevatics.ai</h2> | |
<div class="row justify-content-center"> | |
<div class="col-12"> | |
<div class="card mb-4"> | |
<div class="card-body"> | |
<div class="input-group mb-3"> | |
<input | |
v-model="chartDescription" | |
:disabled="isInputDisabled" | |
class="form-control" | |
placeholder="Describe the chart you want..." | |
aria-label="Chart description" | |
/> | |
<button @click="generateChart" :disabled="isInputDisabled" class="btn generate-button">Generate</button> | |
</div> | |
<div class="chart-output position-relative" :class="{ 'fullscreen': isFullscreen }"> | |
<div class="chart-controls btn-group"> | |
<button @click="toggleFullscreen" class="btn btn-secondary" :title="isFullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'"> | |
<i class="bi" :class="isFullscreen ? 'bi-fullscreen-exit' : 'bi-arrows-fullscreen'"></i> | |
</button> | |
<div class="btn-group"> | |
<button type="button" class="btn btn-secondary " data-bs-toggle="dropdown" aria-expanded="false" title="Download"> | |
<i class="bi bi-download"></i> | |
</button> | |
<ul class="dropdown-menu"> | |
<li><a class="dropdown-item" href="#" @click="downloadChart('png')">Download as PNG</a></li> | |
<li><a class="dropdown-item" href="#" @click="downloadChart('svg')">Download as SVG</a></li> | |
</ul> | |
</div> | |
</div> | |
<div id="mermaid-output" class="border rounded"> | |
<!-- Mermaid chart will be rendered here --> | |
</div> | |
<div v-if="loading" class="loading-overlay" aria-busy="true"> | |
<div class="spinner-border text-primary" role="status"> | |
<span class="visually-hidden">Loading...</span> | |
</div> | |
</div> | |
</div> | |
<div v-if="error" class="alert alert-danger mt-3" role="alert">{{ error }}</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Scripts --> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js"></script> | |
<script src="https://bumbu.me/svg-pan-zoom/dist/svg-pan-zoom.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuid.min.js"></script> | |
<script> | |
mermaid.initialize({ | |
startOnLoad: false, | |
securityLevel: 'strict', | |
theme: 'default' | |
}); | |
const { createApp, ref, onMounted } = Vue; | |
createApp({ | |
setup() { | |
const chartDescription = ref(''); | |
const error = ref(''); | |
const loading = ref(false); | |
const isInputDisabled = ref(false); | |
const isFullscreen = ref(false); | |
const isDarkMode = ref(false); | |
const API_ENDPOINT = 'https://pvanand-audio-chat.hf.space/llm-agent'; | |
const API_KEY = '44d5c2ac18ced6fc25c1e57dcd06fc0b31fb4ad97bf56e67540671a647465df4'; | |
const toggleTheme = () => { | |
isDarkMode.value = !isDarkMode.value; | |
document.body.setAttribute('data-theme', isDarkMode.value ? 'dark' : 'light'); | |
localStorage.setItem('darkMode', isDarkMode.value); | |
updateMermaidTheme(); | |
}; | |
const updateMermaidTheme = () => { | |
mermaid.initialize({ | |
theme: isDarkMode.value ? 'dark' : 'default' | |
}); | |
if (hasChart()) { | |
generateChart(); | |
} | |
}; | |
onMounted(() => { | |
const savedDarkMode = localStorage.getItem('darkMode'); | |
if (savedDarkMode !== null) { | |
isDarkMode.value = JSON.parse(savedDarkMode); | |
document.body.setAttribute('data-theme', isDarkMode.value ? 'dark' : 'light'); | |
updateMermaidTheme(); | |
} | |
}); | |
const hasChart = () => { | |
const container = document.getElementById('mermaid-output'); | |
return container && container.innerHTML.trim() !== ''; | |
}; | |
const generateChart = async () => { | |
if (!chartDescription.value.trim()) { | |
error.value = 'Please enter a chart description.'; | |
return; | |
} | |
loading.value = true; | |
error.value = ''; | |
isInputDisabled.value = true; | |
// Clear the previous chart | |
const container = document.getElementById('mermaid-output'); | |
container.innerHTML = ''; | |
try { | |
const conversationId = uuid.v4(); | |
const response = await axios.post(API_ENDPOINT, { | |
prompt: `Generate Mermaid syntax for the following chart description: ${chartDescription.value} enclosed in <chart> </chart> tags. Respond only with the Mermaid syntax, no additional text.`, | |
system_message: "You are a Mermaid chart Maker. Respond only with the Mermaid syntax enclosed in <chart> </chart> tags. No additional text.", | |
model_id: "anthropic/claude-3.5-sonnet", | |
conversation_id: conversationId, | |
user_id: "ai-chart-generator", | |
}, { | |
headers: { | |
'accept': 'application/json', | |
'X-API-Key': API_KEY, | |
'Content-Type': 'application/json' | |
} | |
}); | |
const chartContent = extractChartContent(response.data); | |
if (chartContent) { | |
await renderMermaid(chartContent); | |
} else { | |
error.value = 'No valid chart content found in the response.'; | |
} | |
} catch (err) { | |
error.value = 'Error generating chart. Please try again.'; | |
console.error('API Error:', err.message); | |
} finally { | |
loading.value = false; | |
isInputDisabled.value = false; | |
} | |
}; | |
const extractChartContent = (responseData) => { | |
const match = responseData.match(/<chart>([\s\S]*?)<\/chart>/); | |
if (match && match[1]) { | |
return match[1].trim().replace(/^```mermaid\n?/, '').replace(/```$/, '').trim(); | |
} | |
return null; | |
}; | |
const renderMermaid = async (input) => { | |
try { | |
const { svg } = await mermaid.render('mySvgId', input); | |
const container = document.getElementById('mermaid-output'); | |
container.innerHTML = svg.replace(/[ ]*max-width:[ 0-9\.]*px;/i , ''); | |
svgPanZoom('#mySvgId', { | |
zoomEnabled: true, | |
controlIconsEnabled: true, | |
fit: true, | |
center: true | |
}); | |
error.value = ''; | |
} catch (err) { | |
error.value = 'Error rendering diagram. Please check your Mermaid syntax.'; | |
console.error('Mermaid Error:', err.message); | |
} | |
}; | |
const toggleFullscreen = () => { | |
const element = document.querySelector('.chart-output'); | |
if (!document.fullscreenElement) { | |
if (element.requestFullscreen) { | |
element.requestFullscreen(); | |
} else if (element.webkitRequestFullscreen) { | |
element.webkitRequestFullscreen(); | |
} else if (element.msRequestFullscreen) { | |
element.msRequestFullscreen(); | |
} | |
} else { | |
if (document.exitFullscreen) { | |
document.exitFullscreen(); | |
} else if (document.webkitExitFullscreen) { | |
document.webkitExitFullscreen(); | |
} else if (document.msExitFullscreen) { | |
document.msExitFullscreen(); | |
} | |
} | |
}; | |
const downloadChart = async (format) => { | |
if (!hasChart()) { | |
console.error('Chart is not ready for download'); | |
return; | |
} | |
const element = document.getElementById('mermaid-output'); | |
const filename = 'chart'; | |
try { | |
switch (format) { | |
case 'png': | |
{ | |
const canvas = await html2canvas(element); | |
const pngData = canvas.toDataURL('image/png'); | |
downloadURI(pngData, `${filename}.png`); | |
} | |
break; | |
case 'svg': | |
{ | |
const svgElement = element.querySelector('svg'); | |
if (!svgElement) throw new Error('SVG element not found'); | |
const svgData = new XMLSerializer().serializeToString(svgElement); | |
const svgBlob = new Blob([svgData], {type: 'image/svg+xml;charset=utf-8'}); | |
const svgUrl = URL.createObjectURL(svgBlob); | |
downloadURI(svgUrl, `${filename}.svg`); | |
URL.revokeObjectURL(svgUrl); | |
} | |
break; | |
} | |
} catch (err) { | |
console.error('Error downloading chart:', err.message); | |
} | |
}; | |
const downloadURI = (uri, name) => { | |
const link = document.createElement('a'); | |
link.download = name; | |
link.href = uri; | |
document.body.appendChild(link); | |
link.click(); | |
document.body.removeChild(link); | |
}; | |
document.addEventListener('fullscreenchange', () => { | |
isFullscreen.value = !!document.fullscreenElement; | |
}); | |
return { | |
chartDescription, | |
error, | |
loading, | |
isInputDisabled, | |
isFullscreen, | |
isDarkMode, | |
generateChart, | |
toggleFullscreen, | |
downloadChart, | |
toggleTheme | |
}; | |
} | |
}).mount('#app'); | |
</script> | |
</body> | |
</html> |