@@ -62,9 +62,10 @@ function M.new(id, mediator)
6262 instance ._response_start_time = 0
6363 instance ._max_response_length = 50000 -- 50KB max response
6464 instance ._headers = {
65- user = (Config .chat and Config .chat .headers and Config .chat .headers .user ) or " ## 👤 You \n\n " ,
66- assistant = (Config .chat and Config .chat .headers and Config .chat .headers .assistant ) or " ## 🤖 ECA \n\n " ,
65+ user = (Config .chat and Config .chat .headers and Config .chat .headers .user ) or " > " ,
66+ assistant = (Config .chat and Config .chat .headers and Config .chat .headers .assistant ) or " " ,
6767 }
68+ instance ._welcome_message_applied = false
6869
6970 require (" eca.observer" ).subscribe (" sidebar-" .. id , function (message )
7071 instance :handle_chat_content (message )
@@ -193,6 +194,7 @@ function M:reset()
193194 self ._selected_code = nil
194195 self ._todos = {}
195196 self ._current_status = " "
197+ self ._welcome_message_applied = false
196198end
197199
198200function M :new_chat ()
@@ -748,6 +750,7 @@ function M:_handle_state_updated(state)
748750
749751 if state .config or state .tools then
750752 self :_update_config_display ()
753+ self :_update_welcome_content ()
751754 end
752755end
753756
@@ -870,26 +873,7 @@ function M:_set_welcome_content()
870873 self ._force_welcome = false
871874 end
872875
873- local lines = {
874- " # 🤖 ECA - Editor Code Assistant" ,
875- " " ,
876- " > **Welcome to ECA!** Your AI-powered code assistant is ready to help." ,
877- " " ,
878- " ## 🚀 Getting Started" ,
879- " " ,
880- " - **Chat**: Type your message in the input field at the bottom and press `Ctrl+S` to send" ,
881- " - **Multiline**: Use `Enter` for new lines, `Ctrl+S` to send" ,
882- " - **Context**: Use `@` to mention files or directories" ,
883- " - **Context**: Use `:EcaAddFile` to add files, `:EcaListContexts` to view, `:EcaClearContexts` to clear" ,
884- " - **Selection**: Use `:EcaAddSelection` to add code selection" ,
885- " - **RepoMap**: Use `:EcaAddRepoMap` to add repository structure context" ,
886- " " ,
887- " ---" ,
888- " " ,
889- }
890-
891- Logger .debug (" Setting welcome content for new chat" )
892- vim .api .nvim_buf_set_lines (chat .bufnr , 0 , - 1 , false , lines )
876+ self :_update_welcome_content ()
893877
894878 -- Auto-add repoMap context if enabled and not already present
895879 if Config .options .context .auto_repo_map then
@@ -1225,6 +1209,41 @@ function M:_update_usage_info()
12251209 )
12261210end
12271211
1212+ function M :_update_welcome_content ()
1213+ if self ._welcome_applied then
1214+ return
1215+ end
1216+
1217+ local chat = self .containers .chat
1218+ if not chat or not vim .api .nvim_buf_is_valid (chat .bufnr ) then
1219+ return
1220+ end
1221+
1222+ local cfg = (Config .chat and Config .chat .welcome ) or {}
1223+ local cfg_msg = (cfg .message and cfg .message ~= " " and cfg .message ) or nil
1224+ local welcome_message = cfg_msg or (self .mediator and self .mediator :welcome_message () or nil )
1225+
1226+ local lines = { " Waiting for welcome message from ECA server..." }
1227+
1228+ if welcome_message and welcome_message ~= " " then
1229+ lines = Utils .split_lines (welcome_message )
1230+
1231+ local tips = cfg .tips or {}
1232+
1233+ if # tips > 0 then
1234+ for _ , tip in ipairs (tips ) do
1235+ table.insert (lines , tip )
1236+ end
1237+ end
1238+
1239+ self ._welcome_applied = true
1240+ end
1241+
1242+ table.insert (lines , " " )
1243+ Logger .debug (" Setting welcome content for chat (welcome applied: " .. tostring (self ._welcome_applied ) .. " )" )
1244+ vim .api .nvim_buf_set_lines (chat .bufnr , 0 , - 1 , false , lines )
1245+ end
1246+
12281247function M :_render_header (container_name , header_text )
12291248 if not Config .windows .sidebar_header .enabled then
12301249 return {}
@@ -1377,8 +1396,32 @@ function M:_handle_streaming_text(text)
13771396 -- Start streaming - simple and direct
13781397 self ._is_streaming = true
13791398 self ._current_response_buffer = " "
1399+
1400+ -- Determine insertion point before adding placeholder (works even with empty header)
1401+ local chat = self .containers .chat
1402+ local start_line = 1
1403+ if chat and vim .api .nvim_buf_is_valid (chat .bufnr ) then
1404+ start_line = vim .api .nvim_buf_line_count (chat .bufnr ) + 1
1405+ end
1406+
1407+ -- Add assistant placeholder and track its start line
13801408 self :_add_message (" assistant" , " " )
1381- self ._last_assistant_line = self :_get_last_message_line ()
1409+ self ._last_assistant_line = start_line
1410+
1411+ -- Track placeholder with an extmark independent of header content
1412+ self .extmarks = self .extmarks or {}
1413+ if not self .extmarks .assistant then
1414+ self .extmarks .assistant = { _ns = vim .api .nvim_create_namespace (' extmarks_assistant' ) }
1415+ end
1416+ if chat and vim .api .nvim_buf_is_valid (chat .bufnr ) then
1417+ self .extmarks .assistant ._id = vim .api .nvim_buf_set_extmark (
1418+ chat .bufnr ,
1419+ self .extmarks .assistant ._ns ,
1420+ start_line - 1 ,
1421+ 0 ,
1422+ { id = self .extmarks .assistant ._id }
1423+ )
1424+ end
13821425 end
13831426
13841427 -- Simple accumulation - no complex checks
@@ -1416,9 +1459,17 @@ function M:_update_streaming_message(content)
14161459 -- Get current lines
14171460 local lines = vim .api .nvim_buf_get_lines (chat .bufnr , 0 , - 1 , false )
14181461 local content_lines = Utils .split_lines (content )
1462+
1463+ -- Resolve assistant start line using extmark if available
14191464 local start_line = self ._last_assistant_line
1465+ if self .extmarks and self .extmarks .assistant and self .extmarks .assistant ._id then
1466+ local pos = vim .api .nvim_buf_get_extmark_by_id (chat .bufnr , self .extmarks .assistant ._ns , self .extmarks .assistant ._id , {})
1467+ if pos and pos [1 ] then
1468+ start_line = pos [1 ] + 1
1469+ end
1470+ end
14201471
1421- Logger .debug (" DEBUG: Assistant line: " .. self ._last_assistant_line .. " , start_line: " .. start_line )
1472+ Logger .debug (" DEBUG: Assistant line: " .. tostring ( self ._last_assistant_line ) .. " , start_line: " .. tostring ( start_line ) )
14221473 Logger .debug (" DEBUG: Content lines: " .. # content_lines )
14231474
14241475 -- Replace assistant content directly
@@ -1440,6 +1491,19 @@ function M:_update_streaming_message(content)
14401491 -- Set all lines at once
14411492 vim .api .nvim_buf_set_lines (chat .bufnr , 0 , - 1 , false , new_lines )
14421493
1494+ -- Re-anchor the assistant extmark at the start line (for subsequent updates)
1495+ self .extmarks = self .extmarks or {}
1496+ if not self .extmarks .assistant then
1497+ self .extmarks .assistant = { _ns = vim .api .nvim_create_namespace (' extmarks_assistant' ) }
1498+ end
1499+ self .extmarks .assistant ._id = vim .api .nvim_buf_set_extmark (
1500+ chat .bufnr ,
1501+ self .extmarks .assistant ._ns ,
1502+ start_line - 1 ,
1503+ 0 ,
1504+ { id = self .extmarks .assistant ._id }
1505+ )
1506+
14431507 Logger .debug (" DEBUG: Buffer updated successfully with " .. # new_lines .. " total lines" )
14441508 end )
14451509
@@ -1521,6 +1585,13 @@ function M:_finalize_streaming_response()
15211585 self ._last_assistant_line = 0
15221586 self ._response_start_time = 0
15231587
1588+ -- Clear assistant placeholder tracking extmark
1589+ local chat = self .containers .chat
1590+ if chat and vim .api .nvim_buf_is_valid (chat .bufnr ) and self .extmarks and self .extmarks .assistant then
1591+ pcall (vim .api .nvim_buf_clear_namespace , chat .bufnr , self .extmarks .assistant ._ns , 0 , - 1 )
1592+ self .extmarks .assistant ._id = nil
1593+ end
1594+
15241595 Logger .debug (" DEBUG: Streaming state cleared" )
15251596 else
15261597 Logger .debug (" DEBUG: _finalize_streaming_response called but not streaming" )
0 commit comments