Explorar o código

fix(tui): use tab layout setting (#40952)

Co-authored-by: Kit Langton <kit.langton@gmail.com>
opencode-agent[bot] hai 1 semana
pai
achega
d7651519f3

+ 6 - 3
packages/core/src/config/plugin/agent.ts

@@ -161,11 +161,14 @@ function isPathAction(action: string): action is PathAction {
 }
 
 function expandHome(resource: string, home: string) {
-  if (resource.startsWith("~/")) return home + resource.slice(1)
   if (resource === "~") return home
   if (resource === "$HOME") return home
-  if (resource.startsWith("$HOME/")) return home + resource.slice(5)
-  if (resource.startsWith("$HOME\\")) return home + resource.slice(5)
+  const relative = resource.startsWith("~/")
+    ? resource.slice(2)
+    : resource.startsWith("$HOME/") || resource.startsWith("$HOME\\")
+      ? resource.slice(6)
+      : undefined
+  if (relative !== undefined) return (path.posix.isAbsolute(home) ? path.posix : path.win32).join(home, relative)
   return resource
 }
 

+ 5 - 0
packages/core/test/config/agent.test.ts

@@ -51,6 +51,11 @@ describe("ConfigAgentPlugin.Plugin", () => {
   it.effect("matches Windows paths against home-relative permissions", () =>
     Effect.gen(function* () {
       const permissions = yield* loadHomePermissions("C:\\Users\\test")
+      expect(permissions).toContainEqual({
+        action: "external_directory",
+        resource: "C:\\Users\\test\\p\\**",
+        effect: "allow",
+      })
       expect(
         Permission.evaluate("external_directory", "C:\\Users\\test\\p\\opencode\\src\\*", permissions).effect,
       ).toBe("allow")

+ 1 - 1
packages/tui/src/app.tsx

@@ -512,7 +512,7 @@ function App(props: { pair?: DialogPairCredentials }) {
   const terminalTitleEnabled = () => config.data.terminal?.title ?? true
   const copyOnSelectEnabled = () => config.data.terminal?.copy_on_select ?? process.platform !== "win32"
   const pasteSummaryEnabled = () => config.data.prompt?.paste !== "full"
-  const tabsVertical = () => (config.data.tabs?.vertical ?? false) && sessionTabsFitVertically(dimensions().width)
+  const tabsVertical = () => config.data.tabs.layout === "vertical" && sessionTabsFitVertically(dimensions().width)
   const tabsVisible = () =>
     sessionTabs.enabled() && (sessionTabs.tabs().length > 0 || sessionTabs.newTab()) && route.data.type !== "plugin"
 

+ 4 - 5
packages/tui/src/component/dialog-config.tsx

@@ -101,12 +101,11 @@ export const settings: Setting[] = [
     labels: ["current directory", "global"],
   },
   {
-    title: "Vertical",
+    title: "Layout",
     category: "Tabs",
-    path: ["tabs", "vertical"],
-    default: false,
-    values: [false, true],
-    labels: ["off", "on"],
+    path: ["tabs", "layout"],
+    default: "horizontal",
+    values: ["horizontal", "vertical"],
     keywords: ["sidebar", "orientation", "left"],
   },
   {

+ 4 - 3
packages/tui/src/config/index.tsx

@@ -132,8 +132,8 @@ export const Info = Schema.Struct({
       scope: Schema.optional(Schema.Literals(["global", "cwd"])).annotate({
         description: "Share tabs globally or keep a separate set for each working directory",
       }),
-      vertical: Schema.optional(Schema.Boolean).annotate({
-        description: "Show tabs in a left sidebar instead of a horizontal strip",
+      layout: Schema.optional(Schema.Literals(["horizontal", "vertical"])).annotate({
+        description: "Show tabs in a horizontal strip or vertical sidebar",
       }),
     }),
   ).annotate({ description: "Tab strip settings" }),
@@ -194,7 +194,7 @@ export type Resolved = Omit<Info, "attention" | "keybinds" | "leader" | "mouse"
   tabs: {
     enabled: boolean
     scope: "global" | "cwd"
-    vertical?: boolean
+    layout: "horizontal" | "vertical"
   }
 }
 
@@ -230,6 +230,7 @@ export function resolve(input: Info, options: { terminalSuspend: boolean }): Res
       ...input.tabs,
       enabled: input.tabs?.enabled ?? true,
       scope: input.tabs?.scope ?? "cwd",
+      layout: input.tabs?.layout ?? "horizontal",
     },
   }
 }

+ 1 - 1
packages/tui/src/routes/session/index.tsx

@@ -204,7 +204,7 @@ export function Session() {
   const availableWidth = createMemo(
     () =>
       dimensions().width -
-      (config.tabs?.enabled && config.tabs.vertical && sessionTabsFitVertically(dimensions().width)
+      (config.tabs?.enabled && config.tabs.layout === "vertical" && sessionTabsFitVertically(dimensions().width)
         ? SESSION_SIDEBAR_WIDTH
         : 0),
   )

+ 6 - 2
packages/tui/test/config-v2.test.tsx

@@ -18,7 +18,10 @@ test("validates mini replay settings", () => {
 test("validates the session tabs setting", () => {
   const decode = Schema.decodeUnknownSync(Info)
 
-  expect(decode({ tabs: { enabled: true, vertical: true } })).toEqual({ tabs: { enabled: true, vertical: true } })
+  expect(decode({ tabs: { enabled: true, layout: "vertical" } })).toEqual({
+    tabs: { enabled: true, layout: "vertical" },
+  })
+  expect(() => decode({ tabs: { layout: true } })).toThrow()
   expect(() => decode({ tabs: { enabled: "on" } })).toThrow()
 })
 
@@ -39,12 +42,13 @@ test("resolves nested config and keybind defaults", () => {
   expect(config.scroll).toEqual({ speed: 2, acceleration: true })
   expect(config.diffs).toEqual({ view: "split" })
   expect(config.debug).toEqual({ devtools: true })
-  expect(config.tabs).toEqual({ enabled: true, scope: "cwd" })
+  expect(config.tabs).toEqual({ enabled: true, scope: "cwd", layout: "horizontal" })
 })
 
 test("shows resolved tab defaults in settings", () => {
   expect(settings.find((setting) => setting.path.join(".") === "tabs.enabled")?.default).toBe(true)
   expect(settings.find((setting) => setting.path.join(".") === "tabs.scope")?.default).toBe("cwd")
+  expect(settings.find((setting) => setting.path.join(".") === "tabs.layout")?.default).toBe("horizontal")
 })
 
 test("provides config and its host interface", async () => {