diff options
| author | Adam <[email protected]> | 2026-01-20 05:06:05 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-01-20 05:39:54 -0600 |
| commit | 0596b02f198ebe43703c3702f45eb2f1acdda431 (patch) | |
| tree | 4714034a023a4e33d59b11931671960ac2c871df /packages | |
| parent | 5145b72c4abd71d51d493d4ee3744fa979d36c8b (diff) | |
| download | opencode-0596b02f198ebe43703c3702f45eb2f1acdda431.tar.gz opencode-0596b02f198ebe43703c3702f45eb2f1acdda431.zip | |
chore: cleanup
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/app/src/pages/layout.tsx | 153 | ||||
| -rw-r--r-- | packages/desktop/src/index.tsx | 23 |
2 files changed, 122 insertions, 54 deletions
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index dc716c030..b46758049 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -76,6 +76,7 @@ export default function Layout(props: ParentProps) { activeWorkspace: undefined as string | undefined, workspaceOrder: {} as Record<string, string[]>, workspaceName: {} as Record<string, string>, + workspaceBranchName: {} as Record<string, Record<string, string>>, workspaceExpanded: {} as Record<string, boolean>, }), ) @@ -198,7 +199,10 @@ export default function Layout(props: ParentProps) { value={editorValue()} class={props.class} onInput={(event) => setEditor("value", event.currentTarget.value)} - onKeyDown={(event) => editorKeyDown(event, props.onSave)} + onKeyDown={(event) => { + event.stopPropagation() + editorKeyDown(event, props.onSave) + }} onBlur={() => closeEditor()} onPointerDown={stopPropagation} onClick={stopPropagation} @@ -458,9 +462,27 @@ export default function Layout(props: ParentProps) { ), ) - const workspaceName = (directory: string) => store.workspaceName[directory] - const workspaceLabel = (directory: string, branch?: string) => - workspaceName(directory) ?? branch ?? getFilename(directory) + const workspaceKey = (directory: string) => directory.replace(/[\\/]+$/, "") + + const workspaceName = (directory: string, projectId?: string, branch?: string) => { + const key = workspaceKey(directory) + const direct = store.workspaceName[key] ?? store.workspaceName[directory] + if (direct) return direct + if (!projectId) return + if (!branch) return + return store.workspaceBranchName[projectId]?.[branch] + } + + const setWorkspaceName = (directory: string, next: string, projectId?: string, branch?: string) => { + const key = workspaceKey(directory) + setStore("workspaceName", (prev) => ({ ...(prev ?? {}), [key]: next })) + if (!projectId) return + if (!branch) return + setStore("workspaceBranchName", projectId, (prev) => ({ ...(prev ?? {}), [branch]: next })) + } + + const workspaceLabel = (directory: string, branch?: string, projectId?: string) => + workspaceName(directory, projectId, branch) ?? branch ?? getFilename(directory) const isWorkspaceEditing = () => editor.active.startsWith("workspace:") @@ -885,10 +907,10 @@ export default function Layout(props: ParentProps) { }) } - const renameWorkspace = (directory: string, next: string) => { - const current = workspaceName(directory) ?? getFilename(directory) + const renameWorkspace = (directory: string, next: string, projectId?: string, branch?: string) => { + const current = workspaceName(directory, projectId, branch) ?? branch ?? getFilename(directory) if (current === next) return - setStore("workspaceName", directory, next) + setWorkspaceName(directory, next, projectId, branch) } function closeProject(directory: string) { @@ -1491,7 +1513,7 @@ export default function Layout(props: ParentProps) { const [workspaceStore] = globalSync.child(directory) const kind = directory === project.worktree ? "local" : "sandbox" - const name = workspaceLabel(directory, workspaceStore.vcs?.branch) + const name = workspaceLabel(directory, workspaceStore.vcs?.branch, project.id) return `${kind} : ${name}` }) @@ -1508,6 +1530,7 @@ export default function Layout(props: ParentProps) { const sortable = createSortable(props.directory) const [workspaceStore, setWorkspaceStore] = globalSync.child(props.directory) const [menuOpen, setMenuOpen] = createSignal(false) + const [pendingRename, setPendingRename] = createSignal(false) const slug = createMemo(() => base64Encode(props.directory)) const sessions = createMemo(() => workspaceStore.session @@ -1517,8 +1540,9 @@ export default function Layout(props: ParentProps) { ) const local = createMemo(() => props.directory === props.project.worktree) const workspaceValue = createMemo(() => { - const name = workspaceStore.vcs?.branch ?? getFilename(props.directory) - return workspaceName(props.directory) ?? name + const branch = workspaceStore.vcs?.branch + const name = branch ?? getFilename(props.directory) + return workspaceName(props.directory, props.project.id, branch) ?? name }) const open = createMemo(() => store.workspaceExpanded[props.directory] ?? true) const loading = createMemo(() => open() && workspaceStore.status !== "complete" && sessions().length === 0) @@ -1537,6 +1561,44 @@ export default function Layout(props: ParentProps) { if (editorOpen(`workspace:${props.directory}`)) closeEditor() } + const header = () => ( + <div class="flex items-center gap-1 min-w-0 flex-1"> + <div class="flex items-center justify-center shrink-0 size-6"> + <Icon name="branch" size="small" /> + </div> + <span class="text-14-medium text-text-base shrink-0">{local() ? "local" : "sandbox"} :</span> + <Show + when={!local()} + fallback={ + <span class="text-14-medium text-text-base min-w-0 truncate"> + {workspaceStore.vcs?.branch ?? getFilename(props.directory)} + </span> + } + > + <InlineEditor + id={`workspace:${props.directory}`} + value={workspaceValue} + onSave={(next) => { + const trimmed = next.trim() + if (!trimmed) return + renameWorkspace(props.directory, trimmed, props.project.id, workspaceStore.vcs?.branch) + setEditor("value", workspaceValue()) + }} + class="text-14-medium text-text-base min-w-0 truncate" + displayClass="text-14-medium text-text-base min-w-0 truncate" + editing={workspaceEditActive()} + stopPropagation={false} + openOnDblClick={false} + /> + </Show> + <Icon + name={open() ? "chevron-down" : "chevron-right"} + size="small" + class="shrink-0 text-icon-base opacity-0 transition-opacity group-hover/workspace:opacity-100 group-focus-within/workspace:opacity-100" + /> + </div> + ) + return ( // @ts-ignore <div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}> @@ -1544,43 +1606,18 @@ export default function Layout(props: ParentProps) { <div class="px-2 py-1"> <div class="group/workspace relative"> <div class="flex items-center gap-1"> - <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"> - <div class="flex items-center gap-1 min-w-0 flex-1"> - <div class="flex items-center justify-center shrink-0 size-6"> - <Icon name="branch" size="small" /> - </div> - <span class="text-14-medium text-text-base shrink-0">{local() ? "local" : "sandbox"} :</span> - <Show - when={!local()} - fallback={ - <span class="text-14-medium text-text-base min-w-0 truncate"> - {workspaceStore.vcs?.branch ?? getFilename(props.directory)} - </span> - } - > - <InlineEditor - id={`workspace:${props.directory}`} - value={workspaceValue} - onSave={(next) => { - const trimmed = next.trim() - if (!trimmed) return - renameWorkspace(props.directory, trimmed) - setEditor("value", workspaceValue()) - }} - class="text-14-medium text-text-base min-w-0 truncate" - displayClass="text-14-medium text-text-base min-w-0 truncate" - editing={workspaceEditActive()} - stopPropagation={false} - openOnDblClick={false} - /> - </Show> - <Icon - name={open() ? "chevron-down" : "chevron-right"} - size="small" - class="shrink-0 text-icon-base opacity-0 transition-opacity group-hover/workspace:opacity-100 group-focus-within/workspace:opacity-100" - /> + <Show + when={workspaceEditActive()} + fallback={ + <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"> + {header()} + </Collapsible.Trigger> + } + > + <div class="flex items-center justify-between w-full pl-2 pr-16 py-1.5 rounded-md"> + {header()} </div> - </Collapsible.Trigger> + </Show> <div class="absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-0.5 transition-opacity" classList={{ @@ -1595,21 +1632,37 @@ export default function Layout(props: ParentProps) { <DropdownMenu.Trigger as={IconButton} icon="dot-grid" variant="ghost" class="size-6 rounded-md" /> </Tooltip> <DropdownMenu.Portal> - <DropdownMenu.Content> + <DropdownMenu.Content + onCloseAutoFocus={(event) => { + if (!pendingRename()) return + event.preventDefault() + setPendingRename(false) + openEditor(`workspace:${props.directory}`, workspaceValue()) + }} + > <DropdownMenu.Item onSelect={() => navigate(`/${slug()}/session`)}> <DropdownMenu.ItemLabel>New session</DropdownMenu.ItemLabel> </DropdownMenu.Item> <DropdownMenu.Item disabled={local()} + onSelect={() => { + setPendingRename(true) + setMenuOpen(false) + }} + > + <DropdownMenu.ItemLabel>Rename</DropdownMenu.ItemLabel> + </DropdownMenu.Item> + <DropdownMenu.Item + disabled={local()} onSelect={() => dialog.show(() => <DialogResetWorkspace directory={props.directory} />)} > - <DropdownMenu.ItemLabel>Reset workspace</DropdownMenu.ItemLabel> + <DropdownMenu.ItemLabel>Reset</DropdownMenu.ItemLabel> </DropdownMenu.Item> <DropdownMenu.Item disabled={local()} onSelect={() => dialog.show(() => <DialogDeleteWorkspace directory={props.directory} />)} > - <DropdownMenu.ItemLabel>Delete workspace</DropdownMenu.ItemLabel> + <DropdownMenu.ItemLabel>Delete</DropdownMenu.ItemLabel> </DropdownMenu.Item> </DropdownMenu.Content> </DropdownMenu.Portal> @@ -1681,7 +1734,7 @@ export default function Layout(props: ParentProps) { const label = (directory: string) => { const [data] = globalSync.child(directory) const kind = directory === props.project.worktree ? "local" : "sandbox" - const name = workspaceLabel(directory, data.vcs?.branch) + const name = workspaceLabel(directory, data.vcs?.branch, props.project.id) return `${kind} : ${name}` } diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index d6ee121af..3b3002967 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -89,11 +89,26 @@ const createPlatform = (password: Accessor<string | null>): Platform => ({ length(): Promise<number> } - const WRITE_DEBOUNCE_MS = 250 + const WRITE_DEBOUNCE_MS = 250 - const storeCache = new Map<string, Promise<StoreLike>>() - const apiCache = new Map<string, AsyncStorage & { flush: () => Promise<void> }>() - const memoryCache = new Map<string, StoreLike>() + const storeCache = new Map<string, Promise<StoreLike>>() + const apiCache = new Map<string, AsyncStorage & { flush: () => Promise<void> }>() + const memoryCache = new Map<string, StoreLike>() + + const flushAll = async () => { + const apis = Array.from(apiCache.values()) + await Promise.all(apis.map((api) => api.flush().catch(() => undefined))) + } + + if ("addEventListener" in globalThis) { + const handleVisibility = () => { + if (document.visibilityState !== "hidden") return + void flushAll() + } + + window.addEventListener("pagehide", () => void flushAll()) + document.addEventListener("visibilitychange", handleVisibility) + } const createMemoryStore = () => { const data = new Map<string, string>() |
