Skip to content

Commit 0596b02

Browse files
committed
chore: cleanup
1 parent 5145b72 commit 0596b02

2 files changed

Lines changed: 122 additions & 54 deletions

File tree

packages/app/src/pages/layout.tsx

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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

packages/desktop/src/index.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,26 @@ const createPlatform = (password: Accessor<string | null>): Platform => ({
8989
length(): Promise<number>
9090
}
9191

92-
const WRITE_DEBOUNCE_MS = 250
92+
const WRITE_DEBOUNCE_MS = 250
9393

94-
const storeCache = new Map<string, Promise<StoreLike>>()
95-
const apiCache = new Map<string, AsyncStorage & { flush: () => Promise<void> }>()
96-
const memoryCache = new Map<string, StoreLike>()
94+
const storeCache = new Map<string, Promise<StoreLike>>()
95+
const apiCache = new Map<string, AsyncStorage & { flush: () => Promise<void> }>()
96+
const memoryCache = new Map<string, StoreLike>()
97+
98+
const flushAll = async () => {
99+
const apis = Array.from(apiCache.values())
100+
await Promise.all(apis.map((api) => api.flush().catch(() => undefined)))
101+
}
102+
103+
if ("addEventListener" in globalThis) {
104+
const handleVisibility = () => {
105+
if (document.visibilityState !== "hidden") return
106+
void flushAll()
107+
}
108+
109+
window.addEventListener("pagehide", () => void flushAll())
110+
document.addEventListener("visibilitychange", handleVisibility)
111+
}
97112

98113
const createMemoryStore = () => {
99114
const data = new Map<string, string>()

0 commit comments

Comments
 (0)