Browse Source

fix(app): preserve agent picker for existing users (#39300)

Brendan Allan 1 tháng trước cách đây
mục cha
commit
f256a4c538

+ 17 - 0
packages/app/src/context/settings.test.ts

@@ -1,6 +1,7 @@
 import { describe, expect, test } from "bun:test"
 import { describe, expect, test } from "bun:test"
 import {
 import {
   hasExistingWebState,
   hasExistingWebState,
+  initialAgentVisibility,
   isAppUpgrade,
   isAppUpgrade,
   layoutTransitionState,
   layoutTransitionState,
   maximumSunsetTimeout,
   maximumSunsetTimeout,
@@ -11,6 +12,22 @@ import {
   shouldEnableNewLayout,
   shouldEnableNewLayout,
 } from "./settings"
 } from "./settings"
 
 
+describe("agent visibility", () => {
+  test("shows the picker for existing profiles and hides it for first-time installs", () => {
+    expect(initialAgentVisibility(undefined, true)).toBe(true)
+    expect(initialAgentVisibility(undefined, false)).toBe(false)
+  })
+
+  test("shows the picker when updating from a recent release", () => {
+    expect(initialAgentVisibility(undefined, false, "1.18.8")).toBe(true)
+  })
+
+  test("preserves the preference after initialization", () => {
+    expect(initialAgentVisibility(true, true, "1.18.8")).toBeUndefined()
+    expect(initialAgentVisibility(true, false)).toBeUndefined()
+  })
+})
+
 describe("layout transition", () => {
 describe("layout transition", () => {
   test("blank profiles default to the new layout", () => {
   test("blank profiles default to the new layout", () => {
     expect(newLayoutDesignsDefault).toBe(true)
     expect(newLayoutDesignsDefault).toBe(true)

+ 27 - 3
packages/app/src/context/settings.tsx

@@ -1,5 +1,5 @@
 import { createStore, reconcile } from "solid-js/store"
 import { createStore, reconcile } from "solid-js/store"
-import { createEffect, createMemo, createSignal, onCleanup } from "solid-js"
+import { batch, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { persisted } from "@/utils/persist"
 import { persisted } from "@/utils/persist"
 import { usePlatform } from "@/context/platform"
 import { usePlatform } from "@/context/platform"
@@ -36,6 +36,7 @@ export interface Settings {
     mobileTitlebarPosition: "top" | "bottom"
     mobileTitlebarPosition: "top" | "bottom"
     newLayoutDesigns?: boolean
     newLayoutDesigns?: boolean
     layoutTransitionEligible?: boolean
     layoutTransitionEligible?: boolean
+    agentVisibilityInitialized?: boolean
     newInterfaceNoticeDismissed?: boolean
     newInterfaceNoticeDismissed?: boolean
     shouldDisplayTabsToast?: boolean
     shouldDisplayTabsToast?: boolean
   }
   }
@@ -93,6 +94,15 @@ export function hasExistingWebState(settings: Promise<string> | string | null, p
   return settings !== null || previousVersion !== undefined
   return settings !== null || previousVersion !== undefined
 }
 }
 
 
+export function initialAgentVisibility(
+  initialized: boolean | undefined,
+  existing: boolean,
+  previousVersion?: string,
+) {
+  if (initialized === true) return
+  return existing || previousVersion !== undefined
+}
+
 export function shouldEnableNewLayout(previous: string | undefined, current: string | undefined) {
 export function shouldEnableNewLayout(previous: string | undefined, current: string | undefined) {
   if (!current) return false
   if (!current) return false
   const currentComparison = compareVersions(current, newLayoutDesignsUpgradeCutoff)
   const currentComparison = compareVersions(current, newLayoutDesignsUpgradeCutoff)
@@ -271,6 +281,18 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
       )
       )
     })
     })
     const visible = (preference: () => boolean) => createMemo(() => !newLayoutDesigns() || preference())
     const visible = (preference: () => boolean) => createMemo(() => !newLayoutDesigns() || preference())
+    const initializeAgentVisibility = (existing: boolean) => {
+      const initial = initialAgentVisibility(
+        store.general?.agentVisibilityInitialized,
+        existing,
+        launchState.previous,
+      )
+      if (initial === undefined) return
+      batch(() => {
+        setStore("general", "showCustomAgents", initial)
+        setStore("general", "agentVisibilityInitialized", true)
+      })
+    }
 
 
     if (sunset && !oldInterfaceRetired()) {
     if (sunset && !oldInterfaceRetired()) {
       const timeout = { current: undefined as ReturnType<typeof setTimeout> | undefined }
       const timeout = { current: undefined as ReturnType<typeof setTimeout> | undefined }
@@ -299,8 +321,9 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
 
 
     createEffect(() => {
     createEffect(() => {
       if (!ready() || !launchState.classified || platform.platform !== "web") return
       if (!ready() || !launchState.classified || platform.platform !== "web") return
-      if (layoutTransitionClassified()) return
-      setStore("general", "layoutTransitionEligible", hasExistingWebState(settingsInit, launchState.previous))
+      const existing = hasExistingWebState(settingsInit, launchState.previous)
+      if (!layoutTransitionClassified()) setStore("general", "layoutTransitionEligible", existing)
+      initializeAgentVisibility(existing)
     })
     })
 
 
     createEffect(() => {
     createEffect(() => {
@@ -426,6 +449,7 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
           if (typeof current === "boolean") return
           if (typeof current === "boolean") return
           setStore("general", "layoutTransitionEligible", eligible)
           setStore("general", "layoutTransitionEligible", eligible)
         },
         },
+        initializeAgentVisibility,
         layoutTransitionAvailable: createMemo(() => ready() && layoutTransition().available),
         layoutTransitionAvailable: createMemo(() => ready() && layoutTransition().available),
         newInterfaceNoticeVisible: createMemo(() => ready() && layoutTransition().notice),
         newInterfaceNoticeVisible: createMemo(() => ready() && layoutTransition().notice),
         dismissNewInterfaceNotice() {
         dismissNewInterfaceNotice() {

+ 1 - 0
packages/desktop/src/renderer/onboarding.tsx

@@ -17,6 +17,7 @@ export function DesktopFirstLaunchOnboarding(props: { initialUrl: string; onLoad
       )
       )
       const existingInstall = await window.api.isOldLayoutEligible()
       const existingInstall = await window.api.isOldLayoutEligible()
       settings.general.setOldLayoutEligible(existingInstall)
       settings.general.setOldLayoutEligible(existingInstall)
+      settings.general.initializeAgentVisibility(existingInstall)
       if (!server.isLocal()) return
       if (!server.isLocal()) return
 
 
       const pending = await window.api.isFirstLaunchOnboardingPending()
       const pending = await window.api.isFirstLaunchOnboardingPending()