Skip to content

fix(app): v2 tab bar does not refresh when session title is generated #30329

@gzb1128

Description

@gzb1128

Description

In the v2 layout desktop/web app, when the LLM generates a session title, the session data is updated (visible in the sidebar session list), but the tab bar does not refresh to show the new title. The tab continues displaying the default title until the user navigates away or triggers a re-render.

Steps to reproduce

  1. Enable v2 layout design in settings (newLayoutDesigns)
  2. Start a new session (default title like "Session xxx" shown in both tab and sidebar)
  3. Send a message that triggers title generation
  4. Observe: the sidebar updates with the generated title, but the tab bar still shows the default title

Root Cause

The tab bar enrichment logic in packages/app/src/components/titlebar.tsx:444-455 uses mapArray to enrich tab data with session info:

const tabsEnriched = iife(() => {
  const base = mapArray(
    () => tabsStore,
    (tab) => {
      const sync = serverSync.createDirSyncContext(tab.dir)
      const session = sync.session.get(tab.sessionId)
      return session ? { ...tab, info: session } : null   // never accesses .title
    },
  )
  return () => base().flatMap((s) => (s ? [s] : []))
})

sync.session.get() calls getSession (packages/app/src/context/directory-sync.ts:202-207) which only tracks:

  • store.session.length
  • store.session[i].id (binary search comparisons)
  • store.session[match.index] (the proxy reference itself)

It does not track store.session[match.index].title. Since SolidJS store proxies use stable references (the proxy at a given index does not change when reconcile updates internal properties), the mapArray item memo never re-runs when the title changes.

The sidebar works correctly because it uses <For each={sessions()}> directly against the store array, where each session prop tracks .title reactively through the proxy.

Proposed Fix

Explicitly access session.title inside the mapArray mapper to create a reactive subscription:

 const tabsEnriched = iife(() => {
   const base = mapArray(
     () => tabsStore,
     (tab) => {
       const sync = serverSync.createDirSyncContext(tab.dir)
       const session = sync.session.get(tab.sessionId)
-      return session ? { ...tab, info: session } : null
+      return session ? { ...tab, info: session, title: session.title } : null
     },
   )
   return () => base().flatMap((s) => (s ? [s] : []))
 })

This ensures the mapArray item memo subscribes to store.session[index].title. When the title changes via session.updated SSE event → reconcile, the memo re-runs and the tab re-renders.

Operating System

macOS / all platforms

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions