From 8dbf140285ec31af0be8b86f49e0b8696d61eb73 Mon Sep 17 00:00:00 2001 From: minhn4 Date: Fri, 28 Aug 2026 09:12:41 +0700 Subject: [PATCH 1/4] feat(app): sidebar account entry with login avatar and logout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an account entry to the sidebar right under Settings: a Log in row (opening the existing configure dialog) while signed out, and an initials avatar while signed in whose menu holds Help (moved out of the sidebar rows) and a new Log out. Log out mirrors codevhub logout: @codev/gateway gains logout() — drop the SSO block from the shared auth file (keeping the gateway api-key and config blocks), leave the force-login sentinel, best-effort revoke both tokens — plus loadAccount(), clearSkillhubCookie() and markForceLogin(). The desktop controller exposes getAccount/logout over new gateway-sso IPC channels, cancelling any in-flight login on logout so every window's account UI resets. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01BcADw33oD8qs5jvn5NWMJx --- CHANGELOG.md | 11 ++ DIVERGENCES.md | 4 +- packages/app/src/components/account-nav.tsx | 126 ++++++++++++++++++ packages/app/src/components/app-sidebar.tsx | 32 +++-- packages/app/src/gateway/types.ts | 15 +++ packages/app/src/i18n/en.ts | 3 + packages/app/src/i18n/es.ts | 3 + packages/app/src/i18n/vi.ts | 3 + packages/codev-gateway/src/auth-file.ts | 66 +++++++++ packages/codev-gateway/src/index.ts | 4 +- packages/codev-gateway/src/sso.ts | 45 ++++++- packages/codev-gateway/test/auth-file.test.ts | 77 +++++++++++ packages/codev-gateway/test/sso.test.ts | 74 +++++++++- .../src/main/gateway-sso/controller.test.ts | 47 +++++++ .../src/main/gateway-sso/controller.ts | 32 +++++ packages/desktop/src/main/gateway-sso/ipc.ts | 2 + packages/desktop/src/preload/index.ts | 2 + 17 files changed, 526 insertions(+), 20 deletions(-) create mode 100644 packages/app/src/components/account-nav.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index d3577cb8fe..43f9c9a818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ file is the human-readable summary of what changed and why. ## Unreleased +### Desktop + +- **Account entry in the sidebar** — the sidebar's bottom cluster now shows, + right under Settings, a **Log in** button while signed out (opening the same + configure dialog as the Log in/Configure menu item) and, once signed in, an + avatar with your initials. The avatar's menu holds **Help** — moved there + from the sidebar, which no longer shows a Help row — and a new **Log out**. + Logging out mirrors `codevhub logout`: it ends the CoDev SSO session (and + revokes its tokens) but keeps the gateway API key, so chat keeps working; + the next sign-in asks for credentials again. + ### Sessions - **Git branch in the session composer** — open sessions now show the current diff --git a/DIVERGENCES.md b/DIVERGENCES.md index f9adc55356..84b2b5559b 100644 --- a/DIVERGENCES.md +++ b/DIVERGENCES.md @@ -139,7 +139,7 @@ from the squash message first, or use a plain merge. | `packages/app/src/components/titlebar.tsx` | Four changes, all to the **v2 titlebar branch only** (`useV2Titlebar()`); the legacy branch below it is untouched. (1) The Home button (and its `home.toggle` command, `mod+b`) opens a new session instead of toggling between `/` and the last active tab: `tabs.toggleHome(...)` becomes a `goHome` that selects an already-open draft if there is one and otherwise falls through to the existing `openNewTab()` project-resolution chain. Its pressed / `aria-pressed` state keys on `layout.route().type === "draft"` rather than `"home"`. The icon, label (`home.title`) and keybind are unchanged, as is `tabs.toggleHome` itself, which is simply no longer called. (2) `` is dropped from that row and the component gains an `export`, so the sidebar can render the DEV/BETA badge at its bottom instead; `Titlebar` keeps its `debugTools` prop because the legacy branch still renders the badge inline. (3) Two buttons added to the row: a sidebar collapse toggle (`sidebar-right` icon, `data-action="titlebar-sidebar-toggle"`, `pressed` while open) as its **first** child, ahead of the `windows() || linux()` `WindowsAppMenu`; and a Search button (`magnifying-glass`, `data-action="titlebar-search"`) immediately after Home, navigating to `/?focus=search`. The toggle reads the fork-only `useAppSidebar()` and is wrapped in ``, which is what keeps it out of the legacy shell. Labels reuse the existing `command.sidebar.toggle` and `home.sessions.search.placeholder` keys — no new i18n. (4) `openNewTab` routes all five of its project-resolution branches through a local `openDraftFor(...)`, which selects the draft already open for that server + directory — via the fork-only `findOpenDraft` in `pages/home-draft-reuse.ts` — and only calls `tabs.newDraft` when there is none. The `+` button gains `data-action="titlebar-new-tab"` so e2e can address it without colliding with the sidebar row, which carries the same accessible name. The model read off the active tab still seeds a *new* draft only; a reused one keeps its own, as selecting its tab by hand would. | The home screen is a new session in this fork, so Home has to go there; toggling into the recent-sessions list contradicted that. Reuse-before-create keeps repeated presses from stacking up draft tabs, which is now the rule for every new-session affordance rather than a Home-only quirk (see the `openNewTab` change and `pages/home-draft-reuse.ts`). Moving the badge lets Home lead the titlebar (measured: x 137 → 92 on macOS, the traffic-light inset). | Keep our `goHome` and the removed indicator. If upstream reworks the titlebar, re-apply both rather than restoring `toggleHome` or the inline badge. If upstream deletes `tabs.toggleHome`, nothing here breaks. Do not remove `Titlebar`'s `debugTools` prop — the legacy titlebar still needs it. Keep `useAppSidebar()` optional-by-design: `useV2Titlebar()` is just `newLayoutDesigns()`, so this titlebar also renders inside the legacy shell, which has no provider — swapping it for `createSimpleContext` would throw there (verified: with the new layout off, the legacy shell renders its own rail, no toggle, and no exception). | | `packages/app/src/context/tabs.tsx` | `newDraft` inserts with `tabs.unshift(tab)` instead of `tabs.push(tab)`, so a draft opens at the head of the tab strip. Session tabs (`addSessionTab`) still append, and nothing else about draft handling changes. | A draft is where you start a session, so it belongs at the front of the strip rather than behind every session already open — with several tabs open the New session tab was scrolling off the right-hand end. | Keep the `unshift`. On conflict take upstream's `newDraft` and swap the insert back. Note the knock-on: `promoteDraft` replaces the draft **at its own index**, so a promoted session keeps the head position and sessions accumulate newest-first. That is intended — the tab you are working in should not jump across the strip the moment you send the first message — but if it is ever unwanted, move the tab in `promoteDraft` rather than reverting this, or the New session tab goes back to the end. | | `packages/app/src/pages/layout-new.tsx` (again) | Mounts the fork-only ``, which on desktop decides one time per boot where the window lands: a window with tabs open on the showing side resumes that side's last session (`layout.home.lastSession`, validated with `sessionAlive` the way the Code/Work switch does), falling back to a session tab already open when that history is unusable; only a window with nothing open lands on a new-session draft (reusing an existing empty draft rather than stacking one up per launch). The chain lives in the exported pure `launchLanding`, tested in the fork-only `packages/app/test-browser/launch-landing.test.ts`, because the desktop-only path cannot be driven from the web e2e harness — the same split `workspace/side.ts`'s `folderEntry` uses. It pins its decision to the pathname the window came up on and disarms as soon as anything navigates, so it can never hijack a later visit to `/` — the sidebar's "View all" links there. Desktop-only: in the browser there is no "launch" distinct from "navigate to /", so redirecting would make the recents screen unreachable by URL and would break two `packages/app/e2e` specs that `goto("/")`. | Launching should put you in front of a prompt, the way Claude Desktop does, rather than on a list. This supersedes the desktop's restore-last-screen behaviour (`getLastActiveUrl`, `packages/desktop/src/renderer/index.tsx`); the previous session stays one click away in Recents and its tab is still restored. | Keep the mount. If upstream restructures this layout, re-add it. Do not "simplify" the boot-window guard into a plain `location.pathname === "/"` check — that is the bug it exists to prevent. | -| `packages/app/src/pages/layout-new.tsx`, `packages/app/src/pages/home.tsx`, `packages/app/src/i18n/{en,es}.ts` | **Persistent app sidebar.** Upstream's project column is an `