personal memory agent
0
fork

Configure Feed

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

Dynamic chat bar placeholder based on onboarding state

Chat bar placeholder text now reflects the user's onboarding state:
- Observing: "I'm learning how you work — ask me what I've noticed..."
- Ready: "I have suggestions for organizing your journal — let's review"
- Interviewing: "Tell me about your work..."
- Default/complete/skipped: "Send a message..."

Implemented via Flask context processor in convey/apps.py — reads
awareness state and injects chat_bar_placeholder into all templates.
Template uses Jinja2 variable with safe fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+86 -1
+21
convey/apps.py
··· 126 126 # Get starred apps list 127 127 starred_apps = config.get("apps", {}).get("starred", []) 128 128 129 + # Chat bar placeholder based on onboarding state 130 + chat_bar_placeholder = "Send a message..." 131 + try: 132 + from think.awareness import get_onboarding 133 + 134 + onboarding = get_onboarding() 135 + onboarding_status = onboarding.get("status", "") 136 + if onboarding_status == "observing": 137 + chat_bar_placeholder = ( 138 + "I'm learning how you work — ask me what I've noticed..." 139 + ) 140 + elif onboarding_status == "ready": 141 + chat_bar_placeholder = ( 142 + "I have suggestions for organizing your journal — let's review" 143 + ) 144 + elif onboarding_status == "interviewing": 145 + chat_bar_placeholder = "Tell me about your work..." 146 + except Exception: 147 + pass # Default placeholder on any error 148 + 129 149 return { 130 150 "app_registry": registry, 131 151 "app": current_app_name, ··· 134 154 "selected_facet": selected_facet, 135 155 "starred_apps": starred_apps, 136 156 "day": day, 157 + "chat_bar_placeholder": chat_bar_placeholder, 137 158 } 138 159 139 160 @app.context_processor
+1 -1
convey/templates/app.html
··· 69 69 <!-- Chat Bar (universal) --> 70 70 <div class="app-bar"> 71 71 <form id="chatInputForm" style="display: contents;"> 72 - <textarea id="chatMessageInput" class="chat-bar-input" rows="1" placeholder="Send a message..."></textarea> 72 + <textarea id="chatMessageInput" class="chat-bar-input" rows="1" placeholder="{{ chat_bar_placeholder|default('Send a message...') }}"></textarea> 73 73 <button type="submit" class="chat-bar-send" title="Send" style="display: none;">↑</button> 74 74 </form> 75 75 <div class="chat-bar-thinking" style="display: none;">
+64
tests/test_observation.py
··· 279 279 assert data[1]["message"] == "finding 4" 280 280 281 281 282 + class TestChatBarPlaceholder: 283 + def _get_placeholder(self): 284 + """Extract chat bar placeholder from the context processor.""" 285 + 286 + from flask import Flask 287 + 288 + app = Flask(__name__) 289 + app.config["TESTING"] = True 290 + 291 + from apps import AppRegistry 292 + from convey.apps import register_app_context 293 + 294 + registry = AppRegistry() 295 + register_app_context(app, registry) 296 + 297 + with app.test_request_context("/"): 298 + # Get context from context processors 299 + ctx = {} 300 + for func in app.template_context_processors[None]: 301 + ctx.update(func()) 302 + return ctx.get("chat_bar_placeholder", "") 303 + 304 + def test_default_placeholder(self): 305 + assert self._get_placeholder() == "Send a message..." 306 + 307 + def test_observing_placeholder(self): 308 + from think.awareness import start_onboarding 309 + 310 + start_onboarding("a") 311 + placeholder = self._get_placeholder() 312 + assert "learning" in placeholder.lower() 313 + assert "noticed" in placeholder.lower() 314 + 315 + def test_ready_placeholder(self): 316 + from think.awareness import start_onboarding, update_state 317 + 318 + start_onboarding("a") 319 + update_state("onboarding", {"status": "ready"}) 320 + placeholder = self._get_placeholder() 321 + assert "suggestions" in placeholder.lower() 322 + 323 + def test_interviewing_placeholder(self): 324 + from think.awareness import start_onboarding 325 + 326 + start_onboarding("b") 327 + placeholder = self._get_placeholder() 328 + assert "work" in placeholder.lower() 329 + 330 + def test_complete_placeholder(self): 331 + from think.awareness import start_onboarding, update_state 332 + 333 + start_onboarding("a") 334 + update_state("onboarding", {"status": "complete"}) 335 + placeholder = self._get_placeholder() 336 + assert placeholder == "Send a message..." 337 + 338 + def test_skipped_placeholder(self): 339 + from think.awareness import skip_onboarding 340 + 341 + skip_onboarding() 342 + placeholder = self._get_placeholder() 343 + assert placeholder == "Send a message..." 344 + 345 + 282 346 class TestChatRedirectMuse: 283 347 def test_redirect_uses_custom_muse(self): 284 348 from unittest.mock import MagicMock, patch