Browse Source

fix(tui): smooth new session tab handoff (#39745)

Kit Langton 2 tuần trước cách đây
mục cha
commit
16b247f756

+ 7 - 1
packages/tui/src/component/session-tabs.tsx

@@ -6,6 +6,7 @@ import { useSessionTabs } from "../context/session-tabs"
 import { useTheme, useThemes } from "../context/theme"
 import {
   adaptiveSessionTabLayout,
+  NEW_SESSION_TAB_TITLE,
   sessionTabComplete,
   seedSessionTabMotion,
   sessionTabOverflowWidth,
@@ -36,7 +37,7 @@ export type SessionTabsController = Pick<ContextController, "tabs" | "current" |
   status(sessionID: string): SessionTabsStatus
 }
 
-const NEW_SESSION_TAB: SessionTab = { sessionID: "new", title: "New session" }
+const NEW_SESSION_TAB: SessionTab = { sessionID: "new", title: NEW_SESSION_TAB_TITLE }
 
 export function SessionTabs(props: { controller?: SessionTabsController; animations?: boolean } = {}) {
   const tabs = props.controller ?? useSessionTabs()
@@ -209,6 +210,11 @@ export function SessionTabs(props: { controller?: SessionTabsController; animati
           createEffect((previous: string) => {
             const next = title()
             if (next === previous) return next
+            if (previous === NEW_SESSION_TAB_TITLE) {
+              setOutgoingTitle(undefined)
+              wipe.jump({ front: 1 })
+              return next
+            }
             setOutgoingTitle(previous)
             wipe.jump({ front: 0 })
             wipe.animate({ front: 1 })

+ 2 - 0
packages/tui/src/context/session-tabs-model.ts

@@ -5,6 +5,8 @@ export type SessionTab = {
 
 export type SessionTabUnread = "activity" | "error"
 
+export const NEW_SESSION_TAB_TITLE = "New session"
+
 export type SessionTabHistory = {
   entries: readonly string[]
   index: number

+ 9 - 2
packages/tui/src/context/session-tabs.tsx

@@ -13,6 +13,7 @@ import {
   cycleSessionTab,
   moveSessionTab,
   moveSessionTabHistory,
+  NEW_SESSION_TAB_TITLE,
   openSessionTab,
   recordClosedSessionTab,
   recordSessionTabHistory,
@@ -77,6 +78,12 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
 
     const root = (sessionID: string) => data.session.root(sessionID)
     const current = () => (route.data.type === "session" ? root(route.data.sessionID) : undefined)
+    const newTab = createMemo((open = false) => {
+      if (route.data.type === "home") return true
+      if (!open) return false
+      const sessionID = current()
+      return sessionID !== undefined && !state().tabs.some((tab) => tab.sessionID === sessionID)
+    }, false)
     const status = (sessionID: string) => {
       const session = root(sessionID)
       const members = data.session.family(session)
@@ -107,7 +114,7 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
       if (route.data.type !== "session" || route.data.sessionID === "dummy") return
       const sessionID = root(route.data.sessionID)
       history = recordSessionTabHistory(history, sessionID)
-      const title = data.session.get(sessionID)?.title
+      const title = data.session.get(sessionID)?.title ?? (newTab() ? NEW_SESSION_TAB_TITLE : undefined)
       const tabs = openSessionTab(state().tabs, { sessionID, title })
       if (tabs === state().tabs && !state().unread[sessionID]) return
       update((draft) => {
@@ -233,7 +240,7 @@ export const { use: useSessionTabs, provider: SessionTabsProvider } = createSimp
         return state().tabs
       },
       newTab() {
-        return route.data.type === "home"
+        return newTab()
       },
       current,
       status,

+ 3 - 0
packages/tui/test/context/session-tabs.test.tsx

@@ -11,6 +11,7 @@ import { DataProvider } from "../../src/context/data"
 import { RouteProvider, useRoute } from "../../src/context/route"
 import { TuiAppProvider } from "../../src/context/runtime"
 import { SessionTabsProvider, useSessionTabs } from "../../src/context/session-tabs"
+import { NEW_SESSION_TAB_TITLE } from "../../src/context/session-tabs-model"
 import { StorageProvider } from "../../src/context/storage"
 import { createApi, createEventStream, createFetch, directory } from "../fixture/tui-client"
 import { TestTuiContexts } from "../fixture/tui-environment"
@@ -142,11 +143,13 @@ test("tracks a temporary new session tab across close and creation", async () =>
     setup.route.navigate({ type: "home" })
     await wait(() => setup.tabs.newTab())
     setup.route.navigate({ type: "session", sessionID: "third" })
+    expect(setup.tabs.newTab()).toBe(true)
     await wait(
       () => setup.tabs.current() === "third" && setup.tabs.tabs().some((tab) => tab.sessionID === "third"),
     )
 
     expect(setup.tabs.newTab()).toBe(false)
+    expect(setup.tabs.tabs().find((tab) => tab.sessionID === "third")?.title).toBe(NEW_SESSION_TAB_TITLE)
   } finally {
     setup.destroy()
   }