Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add bouncing dots thinking animation to AI chat (#174)

scott 068f4143 72c2265e

+25 -10
+22 -8
src/css/app.css
··· 6878 6878 border-bottom-left-radius: 4px; 6879 6879 } 6880 6880 6881 - .ai-chat-bubble--streaming .ai-chat-typing { 6882 - display: inline-block; 6881 + .ai-chat-thinking { 6882 + display: inline-flex; 6883 + align-items: center; 6884 + gap: 4px; 6885 + padding: 2px 0; 6886 + } 6887 + 6888 + .ai-thinking-dot { 6883 6889 width: 6px; 6884 - height: 14px; 6890 + height: 6px; 6891 + border-radius: 50%; 6885 6892 background: var(--color-text-muted); 6886 - border-radius: 1px; 6887 - animation: ai-blink 0.8s step-end infinite; 6888 - vertical-align: text-bottom; 6893 + animation: ai-bounce 1.4s ease-in-out infinite; 6894 + } 6895 + 6896 + .ai-thinking-dot:nth-child(2) { 6897 + animation-delay: 0.16s; 6898 + } 6899 + 6900 + .ai-thinking-dot:nth-child(3) { 6901 + animation-delay: 0.32s; 6889 6902 } 6890 6903 6891 - @keyframes ai-blink { 6892 - 50% { opacity: 0; } 6904 + @keyframes ai-bounce { 6905 + 0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; } 6906 + 40% { transform: scale(1); opacity: 1; } 6893 6907 } 6894 6908 6895 6909 /* Code blocks in AI responses */
+1 -1
src/lib/ai-chat.ts
··· 655 655 656 656 const bubble = document.createElement('div'); 657 657 bubble.className = 'ai-chat-bubble ai-chat-bubble--assistant ai-chat-bubble--streaming'; 658 - bubble.innerHTML = '<span class="ai-chat-typing"></span>'; 658 + bubble.innerHTML = '<span class="ai-chat-thinking"><span class="ai-thinking-dot"></span><span class="ai-thinking-dot"></span><span class="ai-thinking-dot"></span></span>'; 659 659 list.appendChild(bubble); 660 660 list.scrollTop = list.scrollHeight; 661 661
+2 -1
tests/ai-chat.test.ts
··· 631 631 const list = document.createElement('div'); 632 632 const { el } = appendStreamingBubble(list); 633 633 expect(el.className).toContain('ai-chat-bubble--streaming'); 634 - expect(el.querySelector('.ai-chat-typing')).not.toBeNull(); 634 + expect(el.querySelector('.ai-chat-thinking')).not.toBeNull(); 635 + expect(el.querySelectorAll('.ai-thinking-dot')).toHaveLength(3); 635 636 }); 636 637 637 638 it('update replaces content and removes streaming class', () => {