deepshell / index.html
muralipala1504
feat: re-enable HI TTS โ€” code block strip fix verified
b406dd8
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>DeepShell Chat</title>
<!-- Optional: Prism for code highlighting inside responses -->
<link rel="stylesheet" id="prism-theme" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" />
<link rel="stylesheet" id="prism-light-theme" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" disabled />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-docker.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-yaml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-hcl.min.js"></script>
<!-- Optional: marked for Markdown rendering -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.2/marked.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0a0a0a;
--panel: #161616;
--border: #2a2a2a;
--text: #e0e0e0;
--muted: #888888;
--accent: #00a832;
--accent-2: #0e7a9a;
}
:root.light {
--bg: #f8fafc;
--panel: #ffffff;
--border: #e2e8f0;
--text: #1e293b;
--muted: #64748b;
--accent: #16a34a;
--accent-2: #2563eb;
}
* { transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease; }
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: 'JetBrains Mono', 'Courier New', monospace;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 24px 40px;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.brand {
font-weight: 700;
font-size: 18px;
letter-spacing: 1px;
font-family: 'Courier New', monospace;
color: var(--accent);
text-shadow: 0 0 10px #00ff4155;
}
.status {
font-size: 13px;
color: var(--muted);
display: flex;
align-items: center;
gap: 6px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #ef4444;
display: inline-block;
}
.status-dot.ok {
background: #00a832;
box-shadow: none;
}
.header-btn {
padding: 6px 12px;
font-size: 12px;
background: var(--panel);
color: var(--text);
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: all 0.2s;
}
.theme-btn {
padding: 4px 10px;
font-size: 13px;
background: var(--panel);
color: var(--muted);
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: all 0.2s;
}
.theme-btn:hover {
background: var(--border);
color: var(--text);
}
.tts-btn {
padding: 4px 12px;
font-size: 12px;
background: #1a1a1a;
color: #aaaaaa;
border: 1px solid #333333;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
font-family: 'JetBrains Mono', monospace;
transition: all 0.2s;
}
.tts-btn.tts-active {
background: #22c55e;
color: white;
border-color: #16a34a;
box-shadow: 0 0 8px #22c55e55;
}
.mode-toggle {
display: flex;
align-items: center;
gap: 6px;
background: transparent;
border: none;
padding: 0;
}
.mode-btn {
padding: 5px 14px;
font-size: 12px;
border: 1px solid #333333;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
font-family: 'JetBrains Mono', monospace;
transition: all 0.2s;
background: #1a1a1a;
color: #aaaaaa;
letter-spacing: 0.5px;
}
.mode-btn:hover {
color: var(--accent);
border-color: var(--accent);
}
.mode-btn.active {
background: #00a832;
color: #000000;
box-shadow: none;
}
.mode-btn.trainer-active {
background: #f59e0b;
color: white;
}
.header-btn:hover {
background: #334155;
border-color: #475569;
transform: translateY(-1px);
}
.panel {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
padding: 12px;
}
#chat-output {
height: 70vh;
overflow-y: auto;
padding: 16px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
}
.message {
margin: 12px 0;
padding: 14px 18px;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
line-height: 1.7;
font-size: 15px;
}
.message.user-msg {
background: var(--panel);
border-color: #1e2a3a;
border-left: 2px solid #1e3a5a;
}
.message strong {
display: inline-block;
color: #00a832;
margin-bottom: 6px;
}
.message.error {
border-color: #ef4444;
background: var(--bg);
}
.message.error strong {
color: #ef4444;
}
.message.loading {
border-color: #f59e0b;
background: var(--bg);
}
.message.loading strong {
color: #f59e0b;
}
pre {
margin: 6px 0 0;
white-space: pre-wrap;
word-break: break-word;
}
.composer {
margin-top: 16px;
display: grid;
grid-template-columns: 1fr auto;
gap: 12px;
align-items: start;
}
#chat-input {
width: 100%;
resize: vertical;
min-height: 80px;
max-height: 240px;
padding: 14px 16px;
border-radius: 10px;
border: 1px solid var(--border);
background: var(--bg);
color: var(--text);
font-family: inherit;
font-size: 15px;
}
#chat-input:focus {
outline: none;
border-color: #1e3a4a;
box-shadow: none;
}
#chat-send {
height: 40px;
padding: 0 20px;
border: 1px solid var(--accent);
background: transparent;
color: var(--accent);
border-radius: 4px;
cursor: pointer;
font-weight: 700;
font-family: 'Courier New', monospace;
transition: all 0.2s;
letter-spacing: 1px;
}
#chat-send:hover:not(:disabled) {
background: #00a832;
color: #000000;
box-shadow: none;
}
#chat-send:hover:not(:disabled) {
border-color: #334155;
}
#chat-send:disabled {
opacity: 0.6;
cursor: not-allowed;
background: #1a1a1a;
}
.hint {
margin-top: 8px;
color: var(--muted);
font-size: 12px;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* Copy button styles */
.code-wrapper {
position: relative;
margin: 6px 0 0;
}
.copy-btn {
position: absolute;
top: 8px;
right: 8px;
padding: 4px 10px;
font-size: 11px;
background: var(--panel);
color: var(--text);
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
z-index: 10;
font-weight: 600;
}
.copy-btn:hover {
background: #334155;
border-color: #475569;
}
/* Loading spinner */
.spinner {
display: inline-block;
width: 12px;
height: 12px;
border: 2px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin-left: 8px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Command execution styles */
.command-wrapper {
margin: 10px 0;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px;
}
.command-block {
background: var(--panel);
padding: 12px;
border-radius: 6px;
margin: 0 0 8px 0;
font-family: 'Courier New', monospace;
color: #22c55e;
border-left: 3px solid #22c55e;
}
.command-buttons {
display: flex;
gap: 8px;
}
.execute-btn {
padding: 6px 16px;
font-size: 13px;
background: linear-gradient(180deg, #22c55e, #16a34a);
color: white;
border: 1px solid #16a34a;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: all 0.2s;
}
.execute-btn:hover:not(:disabled) {
background: linear-gradient(180deg, #16a34a, #15803d);
transform: translateY(-1px);
}
.execute-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
</style>
</head>
<body>
<div class="container">
<header>
<div class="brand">&gt;_ DeepShell <span id="mode-label" style="font-size:11px;color:var(--muted);font-weight:400;margin-left:6px;"></span></div>
<div style="display: flex; align-items: center; gap: 12px;">
<div class="mode-toggle">
<button class="mode-btn active" id="btn-assistant" onclick="setMode('assistant')">๐Ÿค– Assistant</button>
<button class="mode-btn" id="btn-trainer" onclick="setMode('trainer')">๐ŸŽ“ Trainer</button>
</div>
<button class="theme-btn" id="theme-btn" onclick="toggleTheme()" title="Toggle light/dark theme">๐ŸŒ™</button>
<button id="tts-btn" class="tts-btn" onclick="toggleTTS()" title="Voice OFF โ€” click to enable">๐Ÿ”‡ Voice OFF</button>
<button class="tts-btn" onclick="replayLast()" title="Replay last response">๐Ÿ” Replay</button>
<select id="lang-select" class="header-btn" onchange="setLang(this.value)" title="TTS Language">
<option value="en-US">๐Ÿ‡บ๐Ÿ‡ธ EN</option>
<option value="hi-IN">๐Ÿ‡ฎ๐Ÿ‡ณ HI</option>
<option value="ta-IN">๐Ÿ‡ฎ๐Ÿ‡ณ TA (soon)</option>
<option value="te-IN">๐Ÿ‡ฎ๐Ÿ‡ณ TE (soon)</option>
<option value="ar-SA">๐Ÿ‡ธ๐Ÿ‡ฆ AR (soon)</option>
</select>
<button id="export-btn" class="header-btn" title="Export chat as Markdown">
๐Ÿ“ฅ Export
</button>
<button id="clear-btn" class="header-btn" title="Clear conversation">
๐Ÿ—‘๏ธ Clear
</button>
<a href="/about.html" class="header-btn" style="text-decoration:none;">About</a>
<a href="/contact.html" class="header-btn" style="text-decoration:none;">Contact</a>
<div class="status"><span class="status-dot" id="status-dot"></span>Backend: <span id="status">checkingโ€ฆ</span></div>
</div>
</header>
<div class="panel">
<div id="chat-output" class="panel">
<div id="welcome-msg" style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
padding: 40px 20px;
opacity: 0.85;
">
<div style="font-size: 48px; margin-bottom: 16px;">๐Ÿ–ฅ๏ธ</div>
<div style="font-size: 22px; font-weight: 700; color: var(--accent); margin-bottom: 8px;">&gt;_ DeepShell</div>
<div style="font-size: 13px; color: var(--muted); margin-bottom: 24px; letter-spacing: 1px;">AI-POWERED DEVOPS ASSISTANT & TRAINER</div>
<div style="display: flex; gap: 16px; flex-wrap: wrap; justify-content: center; margin-bottom: 24px;">
<div style="background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 12px 20px; min-width: 140px;">
<div style="font-size: 20px;">๐Ÿค–</div>
<div style="font-size: 13px; font-weight: 600; color: var(--accent); margin: 4px 0;">Assistant</div>
<div style="font-size: 11px; color: var(--muted);">Fast DevOps answers</div>
</div>
<div style="background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 12px 20px; min-width: 140px;">
<div style="font-size: 20px;">๐ŸŽ“</div>
<div style="font-size: 13px; font-weight: 600; color: var(--accent); margin: 4px 0;">Trainer</div>
<div style="font-size: 11px; color: var(--muted);">Learn with examples</div>
</div>
</div>
<div style="font-size: 11px; color: var(--muted); max-width: 900px; line-height: 1.6; text-align: center; white-space: nowrap;">
Linux โ€ข Docker โ€ข Kubernetes โ€ข Terraform โ€ข AWS โ€ข Ansible โ€ข Bash
</div>
<div style="margin-top: 20px; font-size: 11px; color: var(--muted);">
๐Ÿ”Š Voice TTS in English & Hindi &nbsp;|&nbsp; โšก Powered by Groq &nbsp;|&nbsp; ๐Ÿ”’ DevOps-only scope
</div>
</div>
</div>
<form id="chat-form" class="composer" autocomplete="off">
<textarea id="chat-input" placeholder="Type your promptโ€ฆ e.g., Show me all running Docker containers."></textarea>
<button id="chat-send" type="submit">Send</button>
</form>
<div class="hint" style="font-size: 13px; text-align:center;">
๐Ÿ’ก Tip: All code blocks have copy buttons. &nbsp;|&nbsp; Use <b>Trainer mode</b> for structured learning.
</div>
</div>
</div>
<script src="./app.js"></script>
<script>
// simple readiness check
(async () => {
const el = document.getElementById('status');
try {
const r = await fetch('/chat/ready');
const j = await r.json().catch(() => ({}));
const isOk = r.ok && j.status === 'ok';
el.textContent = isOk ? 'ok' : 'error';
document.getElementById('status-dot').classList.toggle('ok', isOk);
} catch {
el.textContent = 'offline';
}
})();
</script>
</body>
</html>