@@ -139,8 +139,11 @@ export type ThreadMeta = ThreadListItem & {
139139 unreadCount : number ;
140140 isAI : boolean ;
141141 isPinned : boolean ;
142+ lastActivityAt ?: number ;
142143} ;
143144
145+ const ACTIVITY_RECENT_MS = 20_000 ;
146+
144147function stripHtml ( value : string ) : string {
145148 if ( ! value ) return "" ;
146149 return value . replace ( / < [ ^ > ] * > / g, "" ) ;
@@ -155,6 +158,7 @@ export interface ChatPanelProps {
155158 project_id : string ;
156159 path : string ;
157160 messages ?: ChatMessages ;
161+ activity ?: any ;
158162 fontSize ?: number ;
159163 desc ?: NodeDesc ;
160164 variant ?: "default" | "compact" ;
@@ -175,6 +179,7 @@ export function ChatPanel({
175179 project_id,
176180 path,
177181 messages,
182+ activity,
178183 fontSize = 13 ,
179184 desc,
180185 variant = "default" ,
@@ -220,6 +225,7 @@ export function ChatPanel({
220225 ) ;
221226 const [ allowAutoSelectThread , setAllowAutoSelectThread ] =
222227 useState < boolean > ( true ) ;
228+ const [ activityNow , setActivityNow ] = useState < number > ( Date . now ( ) ) ;
223229 const submitMentionsRef = useRef < SubmitMentionsFn | undefined > ( undefined ) ;
224230 const scrollToBottomRef = useRef < any > ( null ) ;
225231 const selectedThreadDate = useMemo ( ( ) => {
@@ -276,6 +282,10 @@ export function ChatPanel({
276282 }
277283 llmCacheRef . current . set ( thread . key , isAI ) ;
278284 }
285+ const lastActivityAt =
286+ activity && typeof ( activity as any ) . get === "function"
287+ ? ( activity as any ) . get ( thread . key )
288+ : undefined ;
279289 return {
280290 ...thread ,
281291 displayLabel,
@@ -284,9 +294,11 @@ export function ChatPanel({
284294 unreadCount,
285295 isAI : ! ! isAI ,
286296 isPinned,
297+ lastActivityAt :
298+ typeof lastActivityAt === "number" ? lastActivityAt : undefined ,
287299 } ;
288300 } ) ;
289- } , [ rawThreads , account_id , actions ] ) ;
301+ } , [ rawThreads , account_id , actions , activity ] ) ;
290302
291303 const threadSections = useMemo < ThreadSectionWithUnread [ ] > ( ( ) => {
292304 const grouped = groupThreadsByRecency ( threads ) ;
@@ -335,6 +347,11 @@ export function ChatPanel({
335347 }
336348 } , [ selectedThreadKey ] ) ;
337349
350+ useEffect ( ( ) => {
351+ const id = window . setInterval ( ( ) => setActivityNow ( Date . now ( ) ) , 5000 ) ;
352+ return ( ) => window . clearInterval ( id ) ;
353+ } , [ ] ) ;
354+
338355 useEffect ( ( ) => {
339356 if ( ! fragmentId || isAllThreadsSelected || messages == null ) {
340357 return ;
@@ -509,6 +526,9 @@ export function ChatPanel({
509526 const isHovered = hoveredThread === key ;
510527 const isMenuOpen = openThreadMenuKey === key ;
511528 const showMenu = isHovered || selectedThreadKey === key || isMenuOpen ;
529+ const isRecentlyActive =
530+ thread . lastActivityAt != null &&
531+ activityNow - thread . lastActivityAt < ACTIVITY_RECENT_MS ;
512532 const iconTooltip = thread . isAI
513533 ? "This thread started with an AI request, so the AI responds automatically."
514534 : "This thread started as human-only. AI replies only when explicitly mentioned." ;
@@ -531,6 +551,18 @@ export function ChatPanel({
531551 < Icon name = { isAI ? "robot" : "users" } style = { { color : "#888" } } />
532552 </ Tooltip >
533553 < div style = { THREAD_ITEM_LABEL_STYLE } > { plainLabel } </ div >
554+ { isRecentlyActive && (
555+ < span
556+ aria-label = "Recent activity"
557+ style = { {
558+ width : 8 ,
559+ height : 8 ,
560+ borderRadius : "50%" ,
561+ background : COLORS . BLUE ,
562+ flexShrink : 0 ,
563+ } }
564+ />
565+ ) }
534566 { unreadCount > 0 && ! isHovered && (
535567 < Badge
536568 count = { unreadCount }
@@ -1194,12 +1226,14 @@ export function ChatRoom({
11941226} : EditorComponentProps ) {
11951227 const useEditor = useEditorRedux < ChatState > ( { project_id, path } ) ;
11961228 const messages = useEditor ( "messages" ) as ChatMessages | undefined ;
1229+ const activity = useEditor ( "activity" ) ;
11971230 return (
11981231 < ChatPanel
11991232 actions = { actions }
12001233 project_id = { project_id }
12011234 path = { path }
12021235 messages = { messages }
1236+ activity = { activity as any }
12031237 fontSize = { font_size }
12041238 desc = { desc }
12051239 variant = "default"
0 commit comments