@@ -76,6 +76,7 @@ export default function Layout(props: ParentProps) {
7676 activeWorkspace : undefined as string | undefined ,
7777 workspaceOrder : { } as Record < string , string [ ] > ,
7878 workspaceName : { } as Record < string , string > ,
79+ workspaceBranchName : { } as Record < string , Record < string , string > > ,
7980 workspaceExpanded : { } as Record < string , boolean > ,
8081 } ) ,
8182 )
@@ -198,7 +199,10 @@ export default function Layout(props: ParentProps) {
198199 value = { editorValue ( ) }
199200 class = { props . class }
200201 onInput = { ( event ) => setEditor ( "value" , event . currentTarget . value ) }
201- onKeyDown = { ( event ) => editorKeyDown ( event , props . onSave ) }
202+ onKeyDown = { ( event ) => {
203+ event . stopPropagation ( )
204+ editorKeyDown ( event , props . onSave )
205+ } }
202206 onBlur = { ( ) => closeEditor ( ) }
203207 onPointerDown = { stopPropagation }
204208 onClick = { stopPropagation }
@@ -458,9 +462,27 @@ export default function Layout(props: ParentProps) {
458462 ) ,
459463 )
460464
461- const workspaceName = ( directory : string ) => store . workspaceName [ directory ]
462- const workspaceLabel = ( directory : string , branch ?: string ) =>
463- workspaceName ( directory ) ?? branch ?? getFilename ( directory )
465+ const workspaceKey = ( directory : string ) => directory . replace ( / [ \\ / ] + $ / , "" )
466+
467+ const workspaceName = ( directory : string , projectId ?: string , branch ?: string ) => {
468+ const key = workspaceKey ( directory )
469+ const direct = store . workspaceName [ key ] ?? store . workspaceName [ directory ]
470+ if ( direct ) return direct
471+ if ( ! projectId ) return
472+ if ( ! branch ) return
473+ return store . workspaceBranchName [ projectId ] ?. [ branch ]
474+ }
475+
476+ const setWorkspaceName = ( directory : string , next : string , projectId ?: string , branch ?: string ) => {
477+ const key = workspaceKey ( directory )
478+ setStore ( "workspaceName" , ( prev ) => ( { ...( prev ?? { } ) , [ key ] : next } ) )
479+ if ( ! projectId ) return
480+ if ( ! branch ) return
481+ setStore ( "workspaceBranchName" , projectId , ( prev ) => ( { ...( prev ?? { } ) , [ branch ] : next } ) )
482+ }
483+
484+ const workspaceLabel = ( directory : string , branch ?: string , projectId ?: string ) =>
485+ workspaceName ( directory , projectId , branch ) ?? branch ?? getFilename ( directory )
464486
465487 const isWorkspaceEditing = ( ) => editor . active . startsWith ( "workspace:" )
466488
@@ -885,10 +907,10 @@ export default function Layout(props: ParentProps) {
885907 } )
886908 }
887909
888- const renameWorkspace = ( directory : string , next : string ) => {
889- const current = workspaceName ( directory ) ?? getFilename ( directory )
910+ const renameWorkspace = ( directory : string , next : string , projectId ?: string , branch ?: string ) => {
911+ const current = workspaceName ( directory , projectId , branch ) ?? branch ?? getFilename ( directory )
890912 if ( current === next ) return
891- setStore ( "workspaceName" , directory , next )
913+ setWorkspaceName ( directory , next , projectId , branch )
892914 }
893915
894916 function closeProject ( directory : string ) {
@@ -1491,7 +1513,7 @@ export default function Layout(props: ParentProps) {
14911513
14921514 const [ workspaceStore ] = globalSync . child ( directory )
14931515 const kind = directory === project . worktree ? "local" : "sandbox"
1494- const name = workspaceLabel ( directory , workspaceStore . vcs ?. branch )
1516+ const name = workspaceLabel ( directory , workspaceStore . vcs ?. branch , project . id )
14951517 return `${ kind } : ${ name } `
14961518 } )
14971519
@@ -1508,6 +1530,7 @@ export default function Layout(props: ParentProps) {
15081530 const sortable = createSortable ( props . directory )
15091531 const [ workspaceStore , setWorkspaceStore ] = globalSync . child ( props . directory )
15101532 const [ menuOpen , setMenuOpen ] = createSignal ( false )
1533+ const [ pendingRename , setPendingRename ] = createSignal ( false )
15111534 const slug = createMemo ( ( ) => base64Encode ( props . directory ) )
15121535 const sessions = createMemo ( ( ) =>
15131536 workspaceStore . session
@@ -1517,8 +1540,9 @@ export default function Layout(props: ParentProps) {
15171540 )
15181541 const local = createMemo ( ( ) => props . directory === props . project . worktree )
15191542 const workspaceValue = createMemo ( ( ) => {
1520- const name = workspaceStore . vcs ?. branch ?? getFilename ( props . directory )
1521- return workspaceName ( props . directory ) ?? name
1543+ const branch = workspaceStore . vcs ?. branch
1544+ const name = branch ?? getFilename ( props . directory )
1545+ return workspaceName ( props . directory , props . project . id , branch ) ?? name
15221546 } )
15231547 const open = createMemo ( ( ) => store . workspaceExpanded [ props . directory ] ?? true )
15241548 const loading = createMemo ( ( ) => open ( ) && workspaceStore . status !== "complete" && sessions ( ) . length === 0 )
@@ -1537,50 +1561,63 @@ export default function Layout(props: ParentProps) {
15371561 if ( editorOpen ( `workspace:${ props . directory } ` ) ) closeEditor ( )
15381562 }
15391563
1564+ const header = ( ) => (
1565+ < div class = "flex items-center gap-1 min-w-0 flex-1" >
1566+ < div class = "flex items-center justify-center shrink-0 size-6" >
1567+ < Icon name = "branch" size = "small" />
1568+ </ div >
1569+ < span class = "text-14-medium text-text-base shrink-0" > { local ( ) ? "local" : "sandbox" } :</ span >
1570+ < Show
1571+ when = { ! local ( ) }
1572+ fallback = {
1573+ < span class = "text-14-medium text-text-base min-w-0 truncate" >
1574+ { workspaceStore . vcs ?. branch ?? getFilename ( props . directory ) }
1575+ </ span >
1576+ }
1577+ >
1578+ < InlineEditor
1579+ id = { `workspace:${ props . directory } ` }
1580+ value = { workspaceValue }
1581+ onSave = { ( next ) => {
1582+ const trimmed = next . trim ( )
1583+ if ( ! trimmed ) return
1584+ renameWorkspace ( props . directory , trimmed , props . project . id , workspaceStore . vcs ?. branch )
1585+ setEditor ( "value" , workspaceValue ( ) )
1586+ } }
1587+ class = "text-14-medium text-text-base min-w-0 truncate"
1588+ displayClass = "text-14-medium text-text-base min-w-0 truncate"
1589+ editing = { workspaceEditActive ( ) }
1590+ stopPropagation = { false }
1591+ openOnDblClick = { false }
1592+ />
1593+ </ Show >
1594+ < Icon
1595+ name = { open ( ) ? "chevron-down" : "chevron-right" }
1596+ size = "small"
1597+ class = "shrink-0 text-icon-base opacity-0 transition-opacity group-hover/workspace:opacity-100 group-focus-within/workspace:opacity-100"
1598+ />
1599+ </ div >
1600+ )
1601+
15401602 return (
15411603 // @ts -ignore
15421604 < div use :sortable classList = { { "opacity-30" : sortable . isActiveDraggable } } >
15431605 < Collapsible variant = "ghost" open = { open ( ) } class = "shrink-0" onOpenChange = { openWrapper } >
15441606 < div class = "px-2 py-1" >
15451607 < div class = "group/workspace relative" >
15461608 < div class = "flex items-center gap-1" >
1547- < Collapsible . Trigger class = "flex items-center justify-between w-full pl-2 pr-16 py-1.5 rounded-md hover:bg-surface-raised-base-hover" >
1548- < div class = "flex items-center gap-1 min-w-0 flex-1" >
1549- < div class = "flex items-center justify-center shrink-0 size-6" >
1550- < Icon name = "branch" size = "small" />
1551- </ div >
1552- < span class = "text-14-medium text-text-base shrink-0" > { local ( ) ? "local" : "sandbox" } :</ span >
1553- < Show
1554- when = { ! local ( ) }
1555- fallback = {
1556- < span class = "text-14-medium text-text-base min-w-0 truncate" >
1557- { workspaceStore . vcs ?. branch ?? getFilename ( props . directory ) }
1558- </ span >
1559- }
1560- >
1561- < InlineEditor
1562- id = { `workspace:${ props . directory } ` }
1563- value = { workspaceValue }
1564- onSave = { ( next ) => {
1565- const trimmed = next . trim ( )
1566- if ( ! trimmed ) return
1567- renameWorkspace ( props . directory , trimmed )
1568- setEditor ( "value" , workspaceValue ( ) )
1569- } }
1570- class = "text-14-medium text-text-base min-w-0 truncate"
1571- displayClass = "text-14-medium text-text-base min-w-0 truncate"
1572- editing = { workspaceEditActive ( ) }
1573- stopPropagation = { false }
1574- openOnDblClick = { false }
1575- />
1576- </ Show >
1577- < Icon
1578- name = { open ( ) ? "chevron-down" : "chevron-right" }
1579- size = "small"
1580- class = "shrink-0 text-icon-base opacity-0 transition-opacity group-hover/workspace:opacity-100 group-focus-within/workspace:opacity-100"
1581- />
1609+ < Show
1610+ when = { workspaceEditActive ( ) }
1611+ fallback = {
1612+ < Collapsible . Trigger class = "flex items-center justify-between w-full pl-2 pr-16 py-1.5 rounded-md hover:bg-surface-raised-base-hover" >
1613+ { header ( ) }
1614+ </ Collapsible . Trigger >
1615+ }
1616+ >
1617+ < div class = "flex items-center justify-between w-full pl-2 pr-16 py-1.5 rounded-md" >
1618+ { header ( ) }
15821619 </ div >
1583- </ Collapsible . Trigger >
1620+ </ Show >
15841621 < div
15851622 class = "absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-0.5 transition-opacity"
15861623 classList = { {
@@ -1595,21 +1632,37 @@ export default function Layout(props: ParentProps) {
15951632 < DropdownMenu . Trigger as = { IconButton } icon = "dot-grid" variant = "ghost" class = "size-6 rounded-md" />
15961633 </ Tooltip >
15971634 < DropdownMenu . Portal >
1598- < DropdownMenu . Content >
1635+ < DropdownMenu . Content
1636+ onCloseAutoFocus = { ( event ) => {
1637+ if ( ! pendingRename ( ) ) return
1638+ event . preventDefault ( )
1639+ setPendingRename ( false )
1640+ openEditor ( `workspace:${ props . directory } ` , workspaceValue ( ) )
1641+ } }
1642+ >
15991643 < DropdownMenu . Item onSelect = { ( ) => navigate ( `/${ slug ( ) } /session` ) } >
16001644 < DropdownMenu . ItemLabel > New session</ DropdownMenu . ItemLabel >
16011645 </ DropdownMenu . Item >
1646+ < DropdownMenu . Item
1647+ disabled = { local ( ) }
1648+ onSelect = { ( ) => {
1649+ setPendingRename ( true )
1650+ setMenuOpen ( false )
1651+ } }
1652+ >
1653+ < DropdownMenu . ItemLabel > Rename</ DropdownMenu . ItemLabel >
1654+ </ DropdownMenu . Item >
16021655 < DropdownMenu . Item
16031656 disabled = { local ( ) }
16041657 onSelect = { ( ) => dialog . show ( ( ) => < DialogResetWorkspace directory = { props . directory } /> ) }
16051658 >
1606- < DropdownMenu . ItemLabel > Reset workspace </ DropdownMenu . ItemLabel >
1659+ < DropdownMenu . ItemLabel > Reset</ DropdownMenu . ItemLabel >
16071660 </ DropdownMenu . Item >
16081661 < DropdownMenu . Item
16091662 disabled = { local ( ) }
16101663 onSelect = { ( ) => dialog . show ( ( ) => < DialogDeleteWorkspace directory = { props . directory } /> ) }
16111664 >
1612- < DropdownMenu . ItemLabel > Delete workspace </ DropdownMenu . ItemLabel >
1665+ < DropdownMenu . ItemLabel > Delete</ DropdownMenu . ItemLabel >
16131666 </ DropdownMenu . Item >
16141667 </ DropdownMenu . Content >
16151668 </ DropdownMenu . Portal >
@@ -1681,7 +1734,7 @@ export default function Layout(props: ParentProps) {
16811734 const label = ( directory : string ) => {
16821735 const [ data ] = globalSync . child ( directory )
16831736 const kind = directory === props . project . worktree ? "local" : "sandbox"
1684- const name = workspaceLabel ( directory , data . vcs ?. branch )
1737+ const name = workspaceLabel ( directory , data . vcs ?. branch , props . project . id )
16851738 return `${ kind } : ${ name } `
16861739 }
16871740
0 commit comments