11import { useAtomValue } from "@effect/atom-react" ;
22import { Link } from "@tanstack/react-router" ;
3- import { Fragment } from "react" ;
3+ import { Fragment , useRef } from "react" ;
44
55import { isMac } from "../kernel/platform" ;
6- import { chromeAtom , chromeOf , focusedAtom } from "../wm/chrome" ;
6+ import type { Tab } from "../sdk" ;
7+ import { registry } from "../widgets" ;
8+ import { chromeAtom , chromeOf , focusedAtom , tabOwnerAtom } from "../wm/chrome" ;
79import { deskAtom } from "../wm/instances" ;
810import { type Workspace , workspaceRoute , workspaceTag } from "../wm/workspaces" ;
911import { iconOf } from "./icons" ;
@@ -22,10 +24,22 @@ export function Switcher({ workspaces, active }: SwitcherProps) {
2224 const { instances, order } = useAtomValue ( deskAtom ) ;
2325 const chrome = useAtomValue ( chromeAtom ) ;
2426 const focusedOn = useAtomValue ( focusedAtom ) ;
27+ const owners = useAtomValue ( tabOwnerAtom ) ;
28+ // What each tab showed last, kept while a frame that will set the tab is still loading.
29+ const shown = useRef ( new Map < string , Tab > ( ) ) ;
2530 const showing = ( workspace : Workspace ) => {
31+ const tab = tabOf ( workspace ) ;
32+ shown . current . set ( workspace . id , tab ) ;
33+ return tab ;
34+ } ;
35+ const tabOf = ( workspace : Workspace ) : Tab => {
2636 const tag = workspaceTag ( workspace ) ;
2737 const onWorkspace = ( instance : ( typeof instances ) [ number ] | undefined ) =>
2838 instance !== undefined && ( instance . tags & tag ) !== 0 ;
39+ // The frame that claimed the tab keeps it while it is still on this workspace.
40+ const owner = instances . find ( ( instance ) => instance . id === owners . get ( workspace . id ) ) ;
41+ const claimed = owner !== undefined && onWorkspace ( owner ) ? chrome . get ( owner . id ) ?. tab : undefined ;
42+ if ( claimed !== undefined ) return claimed ;
2943 // Its own last focus once it has been on screen, else its main frame (first in tile order), so
3044 // focusing a frame shared with another workspace never relabels a tab not yet visited.
3145 const remembered = instances . find ( ( instance ) => instance . id === focusedOn . get ( workspace . id ) ) ;
@@ -36,6 +50,8 @@ export function Switcher({ workspaces, active }: SwitcherProps) {
3650 const frame = chrome . get ( focused . id ) ;
3751 // The focused frame decides what the tab says; without a say, the workspace's name over its title.
3852 if ( frame ?. tab !== undefined ) return frame . tab ;
53+ const kept = shown . current . get ( workspace . id ) ;
54+ if ( registry . get ( focused . kind ) ?. ownsTab === true && kept !== undefined ) return kept ;
3955 const title = chromeOf ( focused , frame ) . title ;
4056 // A frame named like its workspace adds nothing to the tab.
4157 return title . toLowerCase ( ) === workspace . name . toLowerCase ( )
0 commit comments