personal memory agent
0
fork

Configure Feed

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

fix(chat): unify chat input on universal chat-bar

Removes the per-day composer in apps/chat/workspace.html and re-enables
the universal chat_bar.html on the chat app. Adjusts the universal bar's
flex sizing so the input fills available width as a single line. Folds
the today/past composer-presence tests into a single universal-bar test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+16 -111
+1 -2
apps/chat/app.json
··· 2 2 "icon": "💬", 3 3 "label": "chat", 4 4 "date_nav": true, 5 - "allow_future_dates": false, 6 - "app_bar": false 5 + "allow_future_dates": false 7 6 }
+8 -15
apps/chat/tests/test_routes.py
··· 99 99 100 100 assert response.status_code == 200 101 101 assert "no chat yet on this day" in html 102 - assert 'id="chatComposerForm"' in html 102 + assert 'id="chatBarForm"' in html 103 103 104 104 105 105 def test_chat_day_renders_all_event_kinds(journal_copy, monkeypatch): ··· 284 284 assert env.client.get("/app/chat/20260101extra").status_code == 404 285 285 286 286 287 - def test_past_day_hides_composer(journal_copy, monkeypatch): 287 + def test_universal_chat_bar_renders_on_today_and_past_day(journal_copy, monkeypatch): 288 288 today = "20990102" 289 289 past_day = "20990101" 290 290 _set_today(monkeypatch, today) 291 291 env = _make_env(journal_copy, monkeypatch) 292 292 293 - html = env.client.get(f"/app/chat/{past_day}").get_data(as_text=True) 293 + today_html = env.client.get(f"/app/chat/{today}").get_data(as_text=True) 294 + past_html = env.client.get(f"/app/chat/{past_day}").get_data(as_text=True) 294 295 295 - assert 'id="chatComposerForm"' not in html 296 - assert "past-day view" in html 297 - 298 - 299 - def test_today_shows_composer(journal_copy, monkeypatch): 300 - today = "20990102" 301 - _set_today(monkeypatch, today) 302 - env = _make_env(journal_copy, monkeypatch) 303 - 304 - html = env.client.get(f"/app/chat/{today}").get_data(as_text=True) 305 - 306 - assert 'id="chatComposerForm"' in html 296 + for html in (today_html, past_html): 297 + assert 'id="chatBarForm"' in html 298 + assert "past-day view" not in html 299 + assert html.count('id="chatBarForm"') == 1
-52
apps/chat/workspace.html
··· 24 24 {% endfor %} 25 25 </ol> 26 26 27 - {% if day == today_day %} 28 - <form id="chatComposerForm" class="chat-composer"> 29 - <textarea id="chatComposerInput" rows="1" placeholder="{{ chat_bar_placeholder }}" aria-label="chat input"></textarea> 30 - <button id="chatComposerSend" type="submit" aria-label="send">send</button> 31 - </form> 32 - {% else %} 33 - <p class="chat-composer-past">past-day view — new messages go to <a href="{{ url_for('app:chat.day', day=today_day) }}">today</a></p> 34 - {% endif %} 35 27 </section> 36 28 37 29 <style> ··· 99 91 live: target.dataset.talentStatus === 'active' 100 92 }); 101 93 }); 102 - 103 - const form = document.getElementById('chatComposerForm'); 104 - if (form) { 105 - const input = document.getElementById('chatComposerInput'); 106 - resizeComposer(input); 107 - input.addEventListener('input', () => resizeComposer(input)); 108 - form.addEventListener('submit', (event) => { 109 - event.preventDefault(); 110 - const text = (input.value || '').trim(); 111 - if (!text) return; 112 - postChat(text, form); 113 - }); 114 - } 115 94 116 95 wireSearch(); 117 96 ··· 366 345 return card; 367 346 } 368 347 369 - async function postChat(text, formNode) { 370 - const button = formNode.querySelector('button[type=submit]'); 371 - const input = formNode.querySelector('textarea'); 372 - button.disabled = true; 373 - input.disabled = true; 374 - try { 375 - const response = await fetch('/api/chat', { 376 - method: 'POST', 377 - headers: { 'Content-Type': 'application/json' }, 378 - body: JSON.stringify({ 379 - message: text, 380 - app: 'chat', 381 - path: window.location.pathname, 382 - facet: window.selectedFacet || '' 383 - }) 384 - }); 385 - if (!response.ok) throw new Error('chat post failed'); 386 - input.value = ''; 387 - resizeComposer(input); 388 - } finally { 389 - button.disabled = false; 390 - input.disabled = false; 391 - input.focus(); 392 - } 393 - } 394 - 395 348 function wireSearch() { 396 349 if (!searchInput || !searchResults) return; 397 350 searchInput.addEventListener('input', () => { ··· 473 426 }); 474 427 475 428 searchResults.appendChild(list); 476 - } 477 - 478 - function resizeComposer(input) { 479 - input.style.height = 'auto'; 480 - input.style.height = Math.min(120, input.scrollHeight) + 'px'; 481 429 } 482 430 483 431 function formatDay(ts) {
+7 -42
convey/static/app.css
··· 1476 1476 } 1477 1477 1478 1478 .chat-bar-status { 1479 - flex: 1 1 auto; 1479 + flex: 0 1 auto; 1480 1480 min-width: 0; 1481 + max-width: 40%; 1481 1482 overflow: hidden; 1482 1483 } 1483 1484 ··· 1501 1502 } 1502 1503 1503 1504 .chat-bar-form { 1505 + flex: 1 1 auto; 1506 + min-width: 0; 1504 1507 display: flex; 1505 1508 align-items: center; 1506 1509 gap: 6px; 1507 1510 } 1508 1511 1509 1512 .chat-bar-input { 1513 + flex: 1 1 auto; 1510 1514 resize: none; 1511 1515 overflow: hidden; 1512 1516 max-height: 120px; 1513 - min-width: 240px; 1517 + min-width: 0; 1518 + width: 100%; 1514 1519 padding: 6px 8px; 1515 1520 border: 1px solid var(--facet-border, #e5e0db); 1516 1521 border-radius: 6px; ··· 2429 2434 2430 2435 .chat-search-result-day, 2431 2436 .chat-search-status, 2432 - .chat-composer-past, 2433 2437 .chat-empty, 2434 2438 .chat-time-sep { 2435 2439 color: #6b7280; ··· 2588 2592 border-radius: 14px; 2589 2593 } 2590 2594 2591 - .chat-composer { 2592 - position: sticky; 2593 - bottom: 0; 2594 - display: flex; 2595 - gap: 8px; 2596 - padding-top: 8px; 2597 - background: linear-gradient(to top, white 70%, rgba(255, 255, 255, 0)); 2598 - } 2599 - 2600 - .chat-composer textarea { 2601 - flex: 1; 2602 - resize: none; 2603 - max-height: 120px; 2604 - padding: 10px 12px; 2605 - border: 1px solid var(--facet-border, #e5e0db); 2606 - border-radius: 12px; 2607 - font: inherit; 2608 - line-height: 1.4; 2609 - background: #fffdf9; 2610 - } 2611 - 2612 - .chat-composer button { 2613 - padding: 10px 14px; 2614 - border: 1px solid var(--facet-border, #e5e0db); 2615 - border-radius: 12px; 2616 - background: white; 2617 - font: inherit; 2618 - cursor: pointer; 2619 - } 2620 - 2621 - .chat-composer button:disabled, 2622 - .chat-composer textarea:disabled { 2623 - opacity: 0.6; 2624 - cursor: not-allowed; 2625 - } 2626 - 2627 2595 .surface-state { 2628 2596 display: flex; 2629 2597 flex-direction: column; ··· 2801 2769 max-width: 88%; 2802 2770 } 2803 2771 2804 - .chat-composer { 2805 - flex-direction: column; 2806 - } 2807 2772 } 2808 2773 2809 2774 @media (prefers-reduced-motion: reduce) {