Skip to content

Commit 24c97fa

Browse files
committed
feat: search thinking block — collapsible search results before AI response
- Search results appear in a collapsible <details> thinking block before AI response - Two-phase UX: spinner shows instantly, populates with results or 'no results' on complete - Source citation pills displayed below thinking block - Removed duplicate inline search details from addAiMessage/handleGroqComplete - Fixed duplicate user message bug in sendToAi() dedup check (querySelectorAll fix) - Added .ai-thinking-block CSS with animations, spinner, no-results state, dark mode
1 parent 19d5d96 commit 24c97fa

5 files changed

Lines changed: 420 additions & 48 deletions

File tree

CHANGELOG-search-thinking-block.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Search Thinking Block — Collapsible Search Results Before AI Response
2+
3+
- Search results now appear in a collapsible "thinking" `<details>` block before the AI response streams
4+
- Thinking block shows immediately with a spinning "Searching…" indicator when search is triggered
5+
- Block populates with actual results (title, snippet, URL) or "No results found" message when search completes
6+
- Source citation pills displayed below the thinking block for quick access to sources
7+
- Removed duplicate inline search details from AI response bubbles (`addAiMessage`, `handleGroqComplete`)
8+
- Fixed: duplicate user message ("YOU" bubble shown twice) when search is enabled — `sendToAi()` dedup check now uses `querySelectorAll` instead of broken `:last-of-type` CSS pseudo-class
9+
- Added `.ai-thinking-block` CSS with green-accented border, slide-in animation, dark mode support
10+
- Added `.ai-thinking-spin` CSS keyframe animation for the search-in-progress spinner
11+
- Added `.ai-thinking-searching` and `.ai-thinking-no-results` styled states
12+
13+
---
14+
15+
## Summary
16+
17+
Refactors the AI chat search flow to show web search results in a collapsible "thinking" block **before** the AI generates its response, providing a transparent agent-like workflow. Also fixes a duplicate user message bug caused by a broken DOM dedup check in `sendToAi()`.
18+
19+
---
20+
21+
## 1. Two-Phase Search Thinking Block
22+
**Files:** `js/ai-chat.js`
23+
**What:** Replaced the old single-shot `renderSearchThinkingBlock()` with a two-phase system: `createSearchThinkingBlock(query)` shows a collapsible block with an animated spinner immediately when search starts, then `populateSearchThinkingBlock(results, query)` fills it with actual results or a "no results" fallback message when the search promise resolves/rejects. The old `ai-search-indicator` element is no longer used — the thinking block replaces it entirely.
24+
**Impact:** Users see immediate visual feedback that search is running, and always see the thinking block regardless of whether DDG returns results.
25+
26+
## 2. Removed Duplicate Search Details from AI Responses
27+
**Files:** `js/ai-chat.js`
28+
**What:** Removed inline search result rendering from `addAiMessage()` and `handleGroqComplete()`, which previously duplicated the search details inside the AI message bubble.
29+
**Impact:** Search results appear only once (in the thinking block above), not twice.
30+
31+
## 3. Fixed Duplicate User Message
32+
**Files:** `js/ai-assistant.js`
33+
**What:** The `sendToAi()` function checked for existing user messages using `.ai-message-user:last-of-type`, which is a CSS pseudo-class that matches the last element of a given tag type (`div`), not the last element with a class. When the thinking block `div` was inserted between the user message and sendToAi's check, the dedup always failed. Fixed by using `querySelectorAll('.ai-message-user .ai-msg-bubble')` and comparing the last element's text content.
34+
**Impact:** User query now appears exactly once in the chat.
35+
36+
## 4. Thinking Block CSS
37+
**Files:** `css/ai-panel.css`
38+
**What:** Added `.ai-thinking-block` container with green-accented border and fade-in animation, `.ai-thinking-spin` rotation keyframe for the search spinner, `.ai-thinking-searching` for the loading state, and `.ai-thinking-no-results` for the empty state with amber info icon. Dark mode variants included.
39+
**Impact:** Consistent, polished visual treatment matching the existing AI panel design.
40+
41+
---
42+
43+
## Files Changed (3 total)
44+
45+
| File | Lines Changed | Type |
46+
|------|:---:|------|
47+
| `js/ai-chat.js` | +217 −48 | Two-phase thinking block, removed inline search duplication |
48+
| `js/ai-assistant.js` | +4 −3 | Fixed user message dedup check |
49+
| `css/ai-panel.css` | +194 −0 | Thinking block styles, spinner, no-results state |

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ TextAgent has undergone significant evolution since its inception. What started
459459

460460
| Date | Commits | Feature / Update |
461461
|------|---------|-----------------|
462+
| **2026-03-13** | | 🔍 **Search thinking block** — web search results now appear in a collapsible "thinking" `<details>` block *before* the AI response streams; two-phase UX: spinner appears instantly when search starts, populates with results (or "no results") when complete; source citation pills below; removed duplicate inline search details from AI response bubbles; fixed duplicate user message bug in `sendToAi()` dedup check |
462463
| **2026-03-13** | | 📐 **LaTeX coding block** — new `📐 LaTeX` toolbar button in Coding dropdown inserts `$$...$$` display math blocks; default template `\frac{\sqrt{2025} + \sqrt{3025}}{\sqrt{25}}` evaluates to 20 via Nerdamer; MathJax renders in preview; Playwright test added |
463464
| **2026-03-13** | | 🐛 **Run All output fix**`▶ Run All` now renders output for every block (bash, JS, SQL, math); fixed `findBlockContainer` producing wrong CSS selectors for bash/JS containers; added `renderBlockOutput()` for DOM rendering during Run All; SQL default template changed to `CREATE TABLE IF NOT EXISTS` for idempotent re-runs; math default template now pre-assigns `x = 5` before `x^2 + 2*x + 1` |
464465
| **2026-03-13** | | 📄 **Feature Showcase update** — synced in-app Feature Showcase template with all recent features; added 7 new rows to Features at a Glance table (Media Embedding, TTS, Game Builder, Finance Dashboard, Disk Workspace, Email to Self, Context Memory); 9 new dedicated sections with examples and tips; AI Document Tags table expanded to 9 tag types; AI model table expanded with Kokoro/Voxtral/Docling/Florence-2; Voice Dictation updated to dual-engine; 14 new task list items; Dev Tooling test count updated to 484 |

css/ai-panel.css

Lines changed: 184 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -618,11 +618,13 @@
618618
margin-bottom: 4px;
619619
background: var(--bg-color);
620620
border: 1px solid var(--border-color);
621-
border-radius: 12px;
621+
border-radius: 8px;
622622
box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.15);
623-
padding: 4px;
623+
padding: 3px;
624624
z-index: 10;
625625
animation: aiModelDropIn 0.15s ease;
626+
max-height: 50vh;
627+
overflow-y: auto;
626628
}
627629

628630
.ai-model-selector.open .ai-model-dropdown {
@@ -644,14 +646,14 @@
644646
.ai-model-option {
645647
display: flex;
646648
align-items: center;
647-
gap: 12px;
649+
gap: 6px;
648650
width: 100%;
649-
padding: 10px 12px;
651+
padding: 3px 8px;
650652
border: none;
651653
background: none;
652654
color: var(--text-color);
653655
cursor: pointer;
654-
border-radius: 8px;
656+
border-radius: 5px;
655657
transition: all 0.15s;
656658
text-align: left;
657659
}
@@ -671,9 +673,9 @@
671673
}
672674

673675
.ai-model-option>i {
674-
font-size: 18px;
676+
font-size: 13px;
675677
color: #667eea;
676-
width: 22px;
678+
width: 16px;
677679
text-align: center;
678680
flex-shrink: 0;
679681
}
@@ -684,14 +686,16 @@
684686

685687
.ai-model-option strong {
686688
display: block;
687-
font-size: 13px;
688-
margin-bottom: 1px;
689+
font-size: 11px;
690+
margin-bottom: 0;
691+
line-height: 1.3;
689692
}
690693

691694
.ai-model-option small {
692695
display: block;
693-
font-size: 11px;
696+
font-size: 9.5px;
694697
opacity: 0.6;
698+
line-height: 1.2;
695699
}
696700

697701
/* --- API Key Modal --- */
@@ -1818,6 +1822,68 @@
18181822
background: rgba(16, 185, 129, 0.08);
18191823
}
18201824

1825+
/* Thinking block — collapsible search results shown before AI response */
1826+
.ai-thinking-block {
1827+
margin: 6px 0 4px;
1828+
padding: 6px 10px;
1829+
border-radius: 10px;
1830+
background: rgba(16, 185, 129, 0.04);
1831+
border: 1px solid rgba(16, 185, 129, 0.12);
1832+
animation: aiThinkIn 0.25s ease;
1833+
}
1834+
1835+
[data-theme="dark"] .ai-thinking-block {
1836+
background: rgba(16, 185, 129, 0.06);
1837+
border-color: rgba(52, 211, 153, 0.15);
1838+
}
1839+
1840+
@keyframes aiThinkIn {
1841+
from { opacity: 0; transform: translateY(6px); }
1842+
to { opacity: 1; transform: translateY(0); }
1843+
}
1844+
1845+
.ai-thinking-block .ai-search-details {
1846+
margin-top: 0;
1847+
}
1848+
1849+
.ai-thinking-block .ai-source-citations {
1850+
margin-top: 4px;
1851+
}
1852+
1853+
/* Thinking block — spinner for "Searching…" state */
1854+
.ai-thinking-spin {
1855+
display: inline-block;
1856+
animation: aiSpin 1s linear infinite;
1857+
}
1858+
1859+
.ai-thinking-searching {
1860+
display: flex;
1861+
align-items: center;
1862+
gap: 6px;
1863+
padding: 6px 8px;
1864+
font-size: 11.5px;
1865+
color: #10b981;
1866+
font-weight: 500;
1867+
}
1868+
1869+
[data-theme="dark"] .ai-thinking-searching {
1870+
color: #34d399;
1871+
}
1872+
1873+
.ai-thinking-no-results {
1874+
display: flex;
1875+
align-items: center;
1876+
gap: 5px;
1877+
padding: 5px 8px;
1878+
font-size: 11px;
1879+
color: var(--text-color);
1880+
opacity: 0.6;
1881+
}
1882+
1883+
.ai-thinking-no-results i {
1884+
color: #f59e0b;
1885+
}
1886+
18211887
/* Search indicator in chat */
18221888
.ai-search-indicator {
18231889
display: flex;
@@ -1864,6 +1930,114 @@
18641930
}
18651931
}
18661932

1933+
/* Collapsible search results details */
1934+
.ai-search-details {
1935+
margin-top: 6px;
1936+
padding: 0 4px;
1937+
font-size: 12px;
1938+
}
1939+
1940+
.ai-search-details summary {
1941+
display: flex;
1942+
align-items: center;
1943+
gap: 5px;
1944+
cursor: pointer;
1945+
color: var(--text-color);
1946+
opacity: 0.7;
1947+
font-size: 11px;
1948+
padding: 4px 8px;
1949+
border-radius: 6px;
1950+
transition: all 0.15s;
1951+
user-select: none;
1952+
list-style: none;
1953+
}
1954+
1955+
.ai-search-details summary::-webkit-details-marker { display: none; }
1956+
1957+
.ai-search-details summary::before {
1958+
content: '▶';
1959+
font-size: 8px;
1960+
transition: transform 0.15s;
1961+
}
1962+
1963+
.ai-search-details[open] summary::before {
1964+
transform: rotate(90deg);
1965+
}
1966+
1967+
.ai-search-details summary:hover {
1968+
opacity: 1;
1969+
background: rgba(16, 185, 129, 0.06);
1970+
}
1971+
1972+
.ai-search-details summary i {
1973+
color: #10b981;
1974+
font-size: 12px;
1975+
}
1976+
1977+
.ai-search-count {
1978+
margin-left: auto;
1979+
font-size: 10px;
1980+
padding: 1px 6px;
1981+
border-radius: 8px;
1982+
background: rgba(16, 185, 129, 0.1);
1983+
color: #10b981;
1984+
}
1985+
1986+
[data-theme="dark"] .ai-search-count {
1987+
color: #34d399;
1988+
background: rgba(52, 211, 153, 0.12);
1989+
}
1990+
1991+
.ai-search-results-list {
1992+
display: flex;
1993+
flex-direction: column;
1994+
gap: 4px;
1995+
margin-top: 6px;
1996+
max-height: 200px;
1997+
overflow-y: auto;
1998+
padding-right: 4px;
1999+
}
2000+
2001+
.ai-search-result-item {
2002+
padding: 5px 8px;
2003+
border-radius: 6px;
2004+
background: var(--button-bg);
2005+
border: 1px solid var(--border-color);
2006+
}
2007+
2008+
.ai-search-result-title a {
2009+
font-size: 11px;
2010+
font-weight: 600;
2011+
color: #667eea;
2012+
text-decoration: none;
2013+
}
2014+
2015+
.ai-search-result-title a:hover {
2016+
text-decoration: underline;
2017+
}
2018+
2019+
[data-theme="dark"] .ai-search-result-title a {
2020+
color: #a5b4fc;
2021+
}
2022+
2023+
.ai-search-result-snippet {
2024+
font-size: 10.5px;
2025+
color: var(--text-color);
2026+
opacity: 0.65;
2027+
line-height: 1.4;
2028+
margin-top: 2px;
2029+
}
2030+
2031+
.ai-search-result-url {
2032+
font-size: 9.5px;
2033+
color: #10b981;
2034+
margin-top: 2px;
2035+
}
2036+
2037+
[data-theme="dark"] .ai-search-result-url {
2038+
color: #34d399;
2039+
}
2040+
18672041
/* Source citations under AI response */
18682042
.ai-source-citations {
18692043
display: flex;

js/ai-assistant.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,10 +1051,11 @@
10511051
const thinkingToggle = document.getElementById('ai-thinking-toggle');
10521052
const enableThinking = thinkingToggle ? thinkingToggle.checked : false;
10531053

1054-
// Show user message in chat (if not already shown)
1054+
// Show user message in chat (if not already shown by sendChatMessage)
10551055
const displayText = userPrompt || `[${taskType}] ${context ? context.substring(0, 80) + '...' : ''}`;
1056-
if (!document.querySelector('.ai-message-user:last-child') ||
1057-
aiChatArea.lastElementChild?.querySelector('.ai-msg-bubble')?.textContent !== displayText) {
1056+
const allUserBubbles = aiChatArea.querySelectorAll('.ai-message-user .ai-msg-bubble');
1057+
const lastUserBubble = allUserBubbles.length > 0 ? allUserBubbles[allUserBubbles.length - 1] : null;
1058+
if (!lastUserBubble || lastUserBubble.textContent.trim() !== displayText.trim()) {
10581059
M._ai.addUserMessage(displayText);
10591060
}
10601061
M._ai.addTypingIndicator();

0 commit comments

Comments
 (0)