update frontend

This commit is contained in:
2026-06-14 17:04:17 +07:00
parent 48c6598fd8
commit be5b156abc
3 changed files with 131 additions and 9 deletions
+41 -8
View File
@@ -1,5 +1,14 @@
const API = '';
let currentSession = null;
let sendTime = 0;
// Configure marked
marked.setOptions({
breaks: true,
gfm: true,
headerIds: false,
mangle: false,
});
// DOM
const sessionList = document.getElementById('session-list');
@@ -97,14 +106,17 @@ async function sendMessage() {
scrollToBottom();
btnSend.disabled = true;
sendTime = performance.now();
try {
const data = await api(`/api/sessions/${currentSession.id}/messages`, {
method: 'POST',
body: JSON.stringify({ content: text }),
});
const elapsed = ((performance.now() - sendTime) / 1000).toFixed(1);
typingEl.remove();
appendMessage('assistant', data.content, data.sources);
appendMessage('assistant', data.content, data.sources, elapsed);
scrollToBottom();
loadSessions();
} catch (e) {
@@ -117,7 +129,15 @@ async function sendMessage() {
}
}
function appendMessage(role, content, sources = null) {
function renderMarkdown(text) {
try {
return marked.parse(text || '');
} catch {
return esc(text);
}
}
function appendMessage(role, content, sources = null, elapsed = null) {
// Remove empty state if present
const empty = messages.querySelector('.empty-state');
if (empty) empty.remove();
@@ -125,14 +145,27 @@ function appendMessage(role, content, sources = null) {
const div = document.createElement('div');
div.className = `message ${role}`;
let html = `<div class="bubble">${esc(content)}</div>`;
let html = '';
if (role === 'assistant') {
html += `<div class="bubble">${renderMarkdown(content)}</div>`;
} else {
html += `<div class="bubble">${esc(content)}</div>`;
}
// Sources + timing
const metaParts = [];
if (sources && sources.length > 0) {
html += `<div class="sources">`;
sources.forEach(s => {
html += `<a class="source-chip" href="${esc(s.url)}" target="_blank" title="${esc(s.title)}">${esc(s.title.substring(0, 40))}</a>`;
});
html += `</div>`;
const chips = sources.map(s =>
`<a class="source-chip" href="${esc(s.url)}" target="_blank" title="${esc(s.title)}">${esc(s.title.substring(0, 40))}</a>`
).join('');
metaParts.push(`<div class="sources">${chips}</div>`);
}
if (elapsed !== null && role === 'assistant') {
metaParts.push(`<div class="msg-meta"><span class="timing">⚡ ${elapsed}s</span></div>`);
}
if (metaParts.length) {
html += metaParts.join('');
}
div.innerHTML = html;
+89 -1
View File
@@ -190,10 +190,98 @@ body {
padding: 12px 16px;
font-size: 14px;
line-height: 1.7;
white-space: pre-wrap;
word-break: break-word;
}
/* Markdown content inside assistant bubble */
.bubble h1, .bubble h2, .bubble h3, .bubble h4 {
margin: 12px 0 6px;
font-weight: 600;
line-height: 1.3;
}
.bubble h1 { font-size: 18px; }
.bubble h2 { font-size: 16px; }
.bubble h3 { font-size: 15px; }
.bubble p { margin: 0 0 8px; }
.bubble p:last-child { margin-bottom: 0; }
.bubble ul, .bubble ol {
margin: 6px 0;
padding-left: 20px;
}
.bubble li { margin: 2px 0; }
.bubble code {
background: #ffffff10;
padding: 1px 5px;
border-radius: 4px;
font-size: 13px;
font-family: 'SF Mono', 'Consolas', 'Menlo', monospace;
}
.bubble pre {
background: #0a0a0a;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 10px 12px;
margin: 8px 0;
overflow-x: auto;
}
.bubble pre code {
background: none;
padding: 0;
font-size: 12px;
line-height: 1.5;
}
.bubble blockquote {
border-left: 3px solid var(--accent);
margin: 8px 0;
padding: 4px 12px;
color: var(--text-dim);
}
.bubble a {
color: #60a5fa;
text-decoration: none;
}
.bubble a:hover { text-decoration: underline; }
.bubble table {
border-collapse: collapse;
margin: 8px 0;
width: 100%;
font-size: 13px;
}
.bubble th, .bubble td {
border: 1px solid var(--border);
padding: 6px 10px;
text-align: left;
}
.bubble th { background: #ffffff08; font-weight: 600; }
.bubble strong { font-weight: 600; }
.bubble em { font-style: italic; }
.message.user .bubble {
white-space: pre-wrap;
}
.msg-meta {
font-size: 11px;
color: var(--text-muted);
padding: 0 4px;
display: flex;
gap: 8px;
align-items: center;
}
.msg-meta .timing {
color: var(--accent);
font-weight: 500;
}
.sources {
display: flex;
flex-wrap: wrap;
+1
View File
@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog RAG Chat</title>
<link rel="stylesheet" href="/static/style.css">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<div class="app">