mirror of https://github.com/garrytan/gstack.git
feat: loading spinner on sidebar open while connecting to server
Shows an amber spinner with "Connecting..." when the sidebar first opens, replacing the empty state. After the first successful /sidebar-chat poll: - If chat history exists: renders it immediately - If no history: shows the welcome message Prevents the jarring empty-then-populated flash on sidebar open. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9d5409c420
commit
4e70c69d10
|
|
@ -158,6 +158,28 @@ body::after {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
.chat-loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-meta);
|
||||||
|
gap: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.chat-loading-spinner {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
border-top-color: var(--amber-500);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
.chat-welcome {
|
.chat-welcome {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,11 @@
|
||||||
<!-- Chat Tab (default, full height) -->
|
<!-- Chat Tab (default, full height) -->
|
||||||
<main id="tab-chat" class="tab-content active">
|
<main id="tab-chat" class="tab-content active">
|
||||||
<div class="chat-messages" id="chat-messages">
|
<div class="chat-messages" id="chat-messages">
|
||||||
<div class="chat-welcome">
|
<div class="chat-loading" id="chat-loading">
|
||||||
|
<div class="chat-loading-spinner"></div>
|
||||||
|
<p>Connecting...</p>
|
||||||
|
</div>
|
||||||
|
<div class="chat-welcome" id="chat-welcome" style="display:none">
|
||||||
<div class="chat-welcome-icon">G</div>
|
<div class="chat-welcome-icon">G</div>
|
||||||
<p>Send a message to Claude Code.</p>
|
<p>Send a message to Claude Code.</p>
|
||||||
<p class="muted">Your agent will see it and act on it.</p>
|
<p class="muted">Your agent will see it and act on it.</p>
|
||||||
|
|
|
||||||
|
|
@ -288,6 +288,8 @@ commandInput.addEventListener('keydown', (e) => {
|
||||||
sendBtn.addEventListener('click', sendMessage);
|
sendBtn.addEventListener('click', sendMessage);
|
||||||
|
|
||||||
// Poll for new chat messages
|
// Poll for new chat messages
|
||||||
|
let initialLoadDone = false;
|
||||||
|
|
||||||
async function pollChat() {
|
async function pollChat() {
|
||||||
if (!serverUrl || !serverToken) return;
|
if (!serverUrl || !serverToken) return;
|
||||||
try {
|
try {
|
||||||
|
|
@ -297,7 +299,21 @@ async function pollChat() {
|
||||||
});
|
});
|
||||||
if (!resp.ok) return;
|
if (!resp.ok) return;
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
|
|
||||||
|
// First successful poll — hide loading spinner
|
||||||
|
if (!initialLoadDone) {
|
||||||
|
initialLoadDone = true;
|
||||||
|
const loading = document.getElementById('chat-loading');
|
||||||
|
const welcome = document.getElementById('chat-welcome');
|
||||||
|
if (loading) loading.style.display = 'none';
|
||||||
|
// Show welcome only if no chat history
|
||||||
|
if (data.total === 0 && welcome) welcome.style.display = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (data.entries && data.entries.length > 0) {
|
if (data.entries && data.entries.length > 0) {
|
||||||
|
// Hide welcome on first real entry
|
||||||
|
const welcome = document.getElementById('chat-welcome');
|
||||||
|
if (welcome) welcome.style.display = 'none';
|
||||||
for (const entry of data.entries) {
|
for (const entry of data.entries) {
|
||||||
addChatEntry(entry);
|
addChatEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
@ -319,7 +335,7 @@ document.getElementById('clear-chat').addEventListener('click', async () => {
|
||||||
agentTextEl = null;
|
agentTextEl = null;
|
||||||
agentText = '';
|
agentText = '';
|
||||||
chatMessages.innerHTML = `
|
chatMessages.innerHTML = `
|
||||||
<div class="chat-welcome">
|
<div class="chat-welcome" id="chat-welcome">
|
||||||
<div class="chat-welcome-icon">G</div>
|
<div class="chat-welcome-icon">G</div>
|
||||||
<p>Send a message to Claude Code.</p>
|
<p>Send a message to Claude Code.</p>
|
||||||
<p class="muted">Your agent will see it and act on it.</p>
|
<p class="muted">Your agent will see it and act on it.</p>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue