diff --git a/packages/app/e2e/regression/terminal-hidden.spec.ts b/packages/app/e2e/regression/terminal-hidden.spec.ts
index 357d9ac203e3..73821580af07 100644
--- a/packages/app/e2e/regression/terminal-hidden.spec.ts
+++ b/packages/app/e2e/regression/terminal-hidden.spec.ts
@@ -7,7 +7,7 @@ const projectID = "proj_hidden_terminal_regression"
const sessionID = "ses_hidden_terminal_regression"
const title = "Hidden terminal regression"
-test("unmounts the terminal renderer while the pane is hidden", async ({ page }) => {
+test("unmounts the terminal panel while it is hidden", async ({ page }) => {
await page.setViewportSize({ width: 1400, height: 900 })
await mockOpenCodeServer(page, {
directory,
@@ -64,13 +64,14 @@ test("unmounts the terminal renderer while the pane is hidden", async ({ page })
await expect(page.locator('[data-component="terminal"]')).toBeVisible()
await page.keyboard.press("Control+Backquote")
- await expect(panel).toHaveAttribute("aria-hidden", "true")
+ await expect(panel).toHaveCount(0)
await expect(page.locator('[data-component="terminal"]')).toHaveCount(0)
await page.setViewportSize({ width: 1200, height: 700 })
await expect(page.locator('[data-component="terminal"]')).toHaveCount(0)
await page.keyboard.press("Control+Backquote")
+ await expect(panel).toBeVisible()
await expect(page.locator('[data-component="terminal"]')).toBeVisible()
})
diff --git a/packages/app/e2e/utils/mock-server.ts b/packages/app/e2e/utils/mock-server.ts
index ce216028fb88..06602c279217 100644
--- a/packages/app/e2e/utils/mock-server.ts
+++ b/packages/app/e2e/utils/mock-server.ts
@@ -1,7 +1,7 @@
import type { Page, Route } from "@playwright/test"
const emptyList = new Set(["/skill", "/command", "/lsp", "/formatter", "/vcs/status", "/vcs/diff"])
-const emptyObject = new Set(["/global/config", "/config", "/provider/auth", "/mcp"])
+const emptyObject = new Set(["/global/config", "/config", "/provider/auth", "/mcp", "/experimental/resource"])
export interface MockServerConfig {
provider: unknown
@@ -55,6 +55,7 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
const path = url.pathname
if (path === "/global/event" || path === "/event") return sse(route, config.events?.(), config.eventRetry)
if (path === "/global/health") return json(route, { healthy: true })
+ if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: false })
if (path === "/permission")
return json(route, typeof config.permissions === "function" ? config.permissions() : (config.permissions ?? []))
if (path === "/question")
@@ -65,6 +66,11 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
return json(route, await config.fileList(url.searchParams.get("path") ?? ""))
if (path === "/file/content" && config.fileContent)
return json(route, await config.fileContent(url.searchParams.get("path") ?? ""))
+ if (path === "/api/reference")
+ return json(route, {
+ location: { directory: config.directory, project: { id: (config.project as { id?: string }).id, directory: config.directory } },
+ data: [],
+ })
if (emptyObject.has(path)) return json(route, {})
if (emptyList.has(path)) return json(route, [])
if (path in staticRoutes) return json(route, staticRoutes[path])
@@ -75,6 +81,9 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
return json(route, session ?? {})
}
+ const projectMatch = path.match(/^\/project\/([^/]+)$/)
+ if (projectMatch) return json(route, config.project)
+
const messageMatch = path.match(/^\/session\/([^/]+)\/message\/([^/]+)$/)
if (messageMatch) {
config.onMessage?.({ sessionID: messageMatch[1]!, messageID: messageMatch[2]! })
diff --git a/packages/app/src/components/session/session-sortable-terminal-tab-v2.tsx b/packages/app/src/components/session/session-sortable-terminal-tab-v2.tsx
new file mode 100644
index 000000000000..f0cdf41ac1f5
--- /dev/null
+++ b/packages/app/src/components/session/session-sortable-terminal-tab-v2.tsx
@@ -0,0 +1,271 @@
+import type { JSX } from "solid-js"
+import { Show, createEffect, onCleanup } from "solid-js"
+import { createStore } from "solid-js/store"
+import { useSortable } from "@dnd-kit/solid/sortable"
+import { IconButton } from "@opencode-ai/ui/icon-button"
+import { Tabs } from "@opencode-ai/ui/tabs"
+import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
+import { Icon } from "@opencode-ai/ui/icon"
+import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
+import { isDefaultTitle as isDefaultTerminalTitle } from "@/context/terminal-title"
+import { useTerminal, type LocalPTY } from "@/context/terminal"
+import { useLanguage } from "@/context/language"
+import { focusTerminalById } from "@/pages/session/helpers"
+
+export function SortableTerminalTabV2(props: {
+ terminal: LocalPTY
+ index: () => number
+ newLayout: boolean
+ onClose?: () => void
+}): JSX.Element {
+ const terminal = useTerminal()
+ const language = useLanguage()
+ const sortable = useSortable({
+ get id() {
+ return props.terminal.id
+ },
+ get index() {
+ return props.index()
+ },
+ })
+ const [store, setStore] = createStore({
+ editing: false,
+ title: props.terminal.title,
+ menuOpen: false,
+ menuPosition: { x: 0, y: 0 },
+ blurEnabled: false,
+ })
+ let input: HTMLInputElement | undefined
+ let blurFrame: number | undefined
+ let editRequested = false
+
+ const isDefaultTitle = () => {
+ const number = props.terminal.titleNumber
+ if (!Number.isFinite(number) || number <= 0) return false
+ return isDefaultTerminalTitle(props.terminal.title, number)
+ }
+
+ const label = () => {
+ language.locale()
+ if (props.terminal.title && !isDefaultTitle()) return props.terminal.title
+
+ const number = props.terminal.titleNumber
+ if (Number.isFinite(number) && number > 0) return language.t("terminal.title.numbered", { number })
+ if (props.terminal.title) return props.terminal.title
+ return language.t("terminal.title")
+ }
+
+ const close = () => {
+ const count = terminal.all().length
+ void terminal.close(props.terminal.id)
+ if (count === 1) {
+ props.onClose?.()
+ }
+ }
+
+ const focus = () => {
+ if (store.editing) return
+ terminal.open(props.terminal.id)
+ if (document.activeElement instanceof HTMLElement) document.activeElement.blur()
+ focusTerminalById(props.terminal.id)
+ }
+
+ const edit = (e?: Event) => {
+ if (e) {
+ e.stopPropagation()
+ e.preventDefault()
+ }
+
+ setStore("blurEnabled", false)
+ setStore("title", props.terminal.title)
+ setStore("editing", true)
+ }
+
+ const save = () => {
+ if (!store.blurEnabled) return
+
+ const value = store.title.trim()
+ if (value && value !== props.terminal.title) {
+ terminal.update({ id: props.terminal.id, title: value })
+ }
+ setStore("editing", false)
+ }
+
+ const keydown = (e: KeyboardEvent) => {
+ if (e.key === "Enter") {
+ e.preventDefault()
+ save()
+ return
+ }
+ if (e.key === "Escape") {
+ e.preventDefault()
+ setStore("editing", false)
+ }
+ }
+
+ const menu = (e: MouseEvent) => {
+ e.preventDefault()
+ setStore("menuPosition", { x: e.clientX, y: e.clientY })
+ setStore("menuOpen", true)
+ }
+
+ createEffect(() => {
+ if (!store.editing) return
+ if (!input) return
+ input.focus()
+ input.select()
+ if (blurFrame !== undefined) cancelAnimationFrame(blurFrame)
+ blurFrame = requestAnimationFrame(() => {
+ blurFrame = undefined
+ setStore("blurEnabled", true)
+ })
+ })
+
+ onCleanup(() => {
+ if (blurFrame === undefined) return
+ cancelAnimationFrame(blurFrame)
+ })
+
+ return (
+
+ }
+ >
+
+
+ {
+ e.stopPropagation()
+ close()
+ }}
+ aria-label={language.t("terminal.close")}
+ />
+ }
+ hideCloseButton
+ onMiddleClick={close}
+ >
+
+ {label()}
+
+
+
+
+ setStore("title", e.currentTarget.value)}
+ onBlur={save}
+ onKeyDown={keydown}
+ onMouseDown={(e) => e.stopPropagation()}
+ class="bg-transparent border-none outline-none min-w-0 flex-1 p-0 text-[13px] leading-4 tracking-[-0.04px] text-v2-text-text-base [font-weight:440] [font-variation-settings:'slnt'_0] [font-variant-numeric:tabular-nums]"
+ />
+
+
+
+
+ {
+ if (!editRequested) return
+ e.preventDefault()
+ editRequested = false
+ requestAnimationFrame(() => edit())
+ }}
+ >
+ (editRequested = true)}>{language.t("common.rename")}
+ {language.t("common.close")}
+
+
+
+
+
+ )
+}
diff --git a/packages/app/src/components/titlebar-tab-strip.tsx b/packages/app/src/components/titlebar-tab-strip.tsx
index 68032e83c998..6705ac1919f0 100644
--- a/packages/app/src/components/titlebar-tab-strip.tsx
+++ b/packages/app/src/components/titlebar-tab-strip.tsx
@@ -17,8 +17,6 @@ import { createTabPromptState } from "@/context/prompt"
import { base64Encode } from "@opencode-ai/core/util/encode"
import { canStartTabDrag, isTabCloseTarget } from "./titlebar-tab-gesture"
-const sortableTransition = { duration: 0 }
-
function SessionTabSlot(props: {
tab: SessionTab
id: string
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index 30b6a5ebce96..db2c1f03e5d6 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -71,11 +71,13 @@ import { type DiffStyle, SessionReviewTab, type SessionReviewTabProps } from "@/
import { useSessionLayout } from "@/pages/session/session-layout"
import { syncSessionModel } from "@/pages/session/session-model-helpers"
import { SessionSidePanel } from "@/pages/session/session-side-panel"
+import { sessionPanelLayout } from "@/pages/session/session-panel-layout"
import { SessionReviewEmptyChangesV2 } from "@opencode-ai/session-ui/v2/session-review-empty-changes-v2"
import { SessionReviewEmptyNoGitV2 } from "@opencode-ai/session-ui/v2/session-review-empty-no-git-v2"
import { ReviewPanelV2 } from "@/pages/session/v2/review-panel-v2"
import { createReviewPanelV2State } from "@/pages/session/v2/review-panel-v2-state"
import { TerminalPanel } from "@/pages/session/terminal-panel"
+import { TerminalPanelV2 } from "@/pages/session/terminal-panel-v2"
import { useComposerCommands } from "@/pages/session/use-composer-commands"
import { useSessionCommands } from "@/pages/session/use-session-commands"
import { useSessionHashScroll } from "@/pages/session/use-session-hash-scroll"
@@ -427,6 +429,12 @@ export default function Page() {
const isDesktop = createMediaQuery("(min-width: 768px)")
const size = createSizing()
const desktopReviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened())
+ const desktopV2ReviewOpen = createMemo(() => newSessionDesign() && desktopReviewOpen() && !!params.id)
+ const terminalOpen = createMemo(() => view().terminal.opened())
+ const desktopTerminalOpen = createMemo(() => isDesktop() && terminalOpen())
+ const desktopInlineTerminalOnlyOpen = createMemo(
+ () => newSessionDesign() && desktopTerminalOpen() && !desktopV2ReviewOpen(),
+ )
const desktopFileTreeOpen = createMemo(
() =>
isDesktop() &&
@@ -435,13 +443,23 @@ export default function Page() {
opened: layout.fileTree.opened(),
}),
)
- const desktopSidePanelOpen = createMemo(() => desktopReviewOpen() || desktopFileTreeOpen())
+ const desktopSessionResizeOpen = createMemo(
+ () => (newSessionDesign() ? desktopV2ReviewOpen() || desktopTerminalOpen() : desktopReviewOpen()),
+ )
+ const desktopSidePanelOpen = createMemo(() => desktopSessionResizeOpen() || desktopFileTreeOpen())
const sessionPanelWidth = createMemo(() => {
if (!desktopSidePanelOpen()) return "100%"
- if (desktopReviewOpen()) return `${layout.session.width()}px`
+ if (desktopSessionResizeOpen()) return `${layout.session.width()}px`
return `calc(100% - ${layout.fileTree.width()}px)`
})
const centered = createMemo(() => isDesktop() && !desktopReviewOpen())
+ const desktopV2PanelLayout = createMemo(() =>
+ sessionPanelLayout({
+ review: desktopV2ReviewOpen(),
+ terminal: desktopTerminalOpen(),
+ files: desktopFileTreeOpen(),
+ }),
+ )
function normalizeTab(tab: string) {
if (!tab.startsWith("file://")) return tab
@@ -2093,7 +2111,7 @@ export default function Page() {
classList={{
"@container relative shrink-0 flex flex-col min-h-0 h-full flex-1 md:flex-none transition-[width]": true,
"duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
- !size.active() && !ui.reviewSnap,
+ !size.active() && !ui.reviewSnap && !desktopInlineTerminalOnlyOpen(),
}}
style={{
width: sessionPanelWidth(),
@@ -2113,7 +2131,7 @@ export default function Page() {
)}
-
+
size.start()}>
- (newSessionDesign() ? reviewPanelV2() : reviewPanel())}
- activeDiff={tree.activeDiff}
- focusReviewDiff={focusReviewDiff}
- reviewSnap={ui.reviewSnap}
- size={size}
- />
+
+
+
+
+
+
+
+
+
+
+
+
+ size.start()}>
+ {
+ size.touch()
+ layout.terminal.resize(height)
+ }}
+ onCollapse={() => view().terminal.close()}
+ />
+
+
+
+
+
+
+
+
+
+
-
+
+
+
)
}
diff --git a/packages/app/src/pages/session/session-panel-layout.test.ts b/packages/app/src/pages/session/session-panel-layout.test.ts
new file mode 100644
index 000000000000..499dd7411a0b
--- /dev/null
+++ b/packages/app/src/pages/session/session-panel-layout.test.ts
@@ -0,0 +1,19 @@
+import { describe, expect, test } from "bun:test"
+import { sessionPanelLayout } from "./session-panel-layout"
+
+describe("sessionPanelLayout", () => {
+ test("keeps one V2 owner while changing panel geometry", () => {
+ expect(sessionPanelLayout({ review: false, terminal: false, files: false })).toEqual({
+ visible: false,
+ stacked: false,
+ })
+ expect(sessionPanelLayout({ review: false, terminal: true, files: false })).toEqual({
+ visible: true,
+ stacked: false,
+ })
+ expect(sessionPanelLayout({ review: true, terminal: true, files: false })).toEqual({
+ visible: true,
+ stacked: true,
+ })
+ })
+})
diff --git a/packages/app/src/pages/session/session-panel-layout.ts b/packages/app/src/pages/session/session-panel-layout.ts
new file mode 100644
index 000000000000..49feaebedeef
--- /dev/null
+++ b/packages/app/src/pages/session/session-panel-layout.ts
@@ -0,0 +1,6 @@
+export function sessionPanelLayout(input: { review: boolean; terminal: boolean; files: boolean }) {
+ return {
+ visible: input.review || input.terminal || input.files,
+ stacked: input.review && input.terminal,
+ }
+}
diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx
index 097df2219376..690262102039 100644
--- a/packages/app/src/pages/session/session-side-panel.tsx
+++ b/packages/app/src/pages/session/session-side-panel.tsx
@@ -51,6 +51,7 @@ export function SessionSidePanel(props: {
focusReviewDiff: (path: string) => void
reviewSnap: boolean
size: Sizing
+ stacked?: boolean
}) {
const layout = useLayout()
const settings = useSettings()
@@ -219,8 +220,10 @@ export function SessionSidePanel(props: {
aria-label={language.t("session.panel.reviewAndFiles")}
aria-hidden={!open()}
inert={!open()}
- class="relative min-w-0 h-full flex shrink-0 overflow-hidden bg-background-base"
+ class="relative min-w-0 flex overflow-hidden bg-background-base"
classList={{
+ "h-full shrink-0": !props.stacked,
+ "min-h-0 flex-1": props.stacked,
"pointer-events-none": !open(),
"transition-[width] duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!props.size.active() && !props.reviewSnap,
diff --git a/packages/app/src/pages/session/terminal-panel-v2.tsx b/packages/app/src/pages/session/terminal-panel-v2.tsx
new file mode 100644
index 000000000000..aec999d5dc15
--- /dev/null
+++ b/packages/app/src/pages/session/terminal-panel-v2.tsx
@@ -0,0 +1,364 @@
+import { For, Show, createEffect, createMemo, on, onCleanup, onMount } from "solid-js"
+import { createStore } from "solid-js/store"
+import { makeEventListener } from "@solid-primitives/event-listener"
+import { createMediaQuery } from "@solid-primitives/media"
+import { DragDropProvider, PointerSensor } from "@dnd-kit/solid"
+import { isSortable } from "@dnd-kit/solid/sortable"
+import { Accessibility, AutoScroller, Feedback, PointerActivationConstraints } from "@dnd-kit/dom"
+import { RestrictToHorizontalAxis } from "@dnd-kit/abstract/modifiers"
+import { RestrictToElement } from "@dnd-kit/dom/modifiers"
+import { Tabs } from "@opencode-ai/ui/tabs"
+import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
+import { IconButton } from "@opencode-ai/ui/icon-button"
+import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
+import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
+import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
+
+import { SortableTerminalTabV2 } from "@/components/session/session-sortable-terminal-tab-v2"
+import { Terminal } from "@/components/terminal"
+import { useCommand } from "@/context/command"
+import { useLanguage } from "@/context/language"
+import { useLayout } from "@/context/layout"
+import { useSettings } from "@/context/settings"
+import { useTerminal } from "@/context/terminal"
+import { useSDK } from "@/context/sdk"
+import { terminalTabLabel } from "@/pages/session/terminal-label"
+import { createSizing, focusTerminalById } from "@/pages/session/helpers"
+import { getTerminalHandoff, setTerminalHandoff } from "@/pages/session/handoff"
+import { useSessionLayout } from "@/pages/session/session-layout"
+
+export function TerminalPanelV2(props: { stacked?: boolean } = {}) {
+ const delays = [120, 240]
+ const layout = useLayout()
+ const terminal = useTerminal()
+ const sdk = useSDK()
+ const language = useLanguage()
+ const command = useCommand()
+ const settings = useSettings()
+ const { workspaceKey, view } = useSessionLayout()
+
+ const isDesktop = createMediaQuery("(min-width: 768px)")
+ const newLayout = createMemo(() => settings.general.newLayoutDesigns())
+ const opened = createMemo(() => view().terminal.opened())
+ const size = createSizing()
+ const height = createMemo(() => layout.terminal.height())
+ const close = () => view().terminal.close()
+ let root: HTMLDivElement | undefined
+ let tabList: HTMLDivElement | undefined
+
+ const [store, setStore] = createStore({
+ autoCreated: false,
+ recovered: {} as Record,
+ view: typeof window === "undefined" ? 1000 : (window.visualViewport?.height ?? window.innerHeight),
+ })
+
+ const max = () => store.view * 0.6
+ const pane = () => Math.min(height(), max())
+ const stacked = createMemo(() => isDesktop() && props.stacked)
+ const panelHeight = createMemo(() => (isDesktop() ? (stacked() ? `${pane()}px` : "100%") : opened() ? `${pane()}px` : "0px"))
+ const contentHeight = createMemo(() => (isDesktop() ? (stacked() ? `${pane()}px` : "100%") : `${pane()}px`))
+ const newTerminalKeybind = createMemo(() => command.keybindParts("terminal.new"))
+
+ onMount(() => {
+ if (typeof window === "undefined") return
+
+ const sync = () => setStore("view", window.visualViewport?.height ?? window.innerHeight)
+ const port = window.visualViewport
+
+ sync()
+ makeEventListener(window, "resize", sync)
+ if (port) makeEventListener(port, "resize", sync)
+ })
+
+ createEffect(() => {
+ if (!opened()) {
+ setStore("autoCreated", false)
+ return
+ }
+
+ if (!terminal.ready() || terminal.all().length !== 0 || store.autoCreated) return
+ terminal.new()
+ setStore("autoCreated", true)
+ })
+
+ createEffect(
+ on(
+ () => terminal.all().length,
+ (count, prevCount) => {
+ if (prevCount === undefined || prevCount <= 0 || count !== 0) return
+ if (!opened()) return
+ close()
+ },
+ ),
+ )
+
+ const focus = (id: string) => {
+ focusTerminalById(id)
+
+ const frame = requestAnimationFrame(() => {
+ if (!opened()) return
+ if (terminal.active() !== id) return
+ focusTerminalById(id)
+ })
+
+ const timers = delays.map((ms) =>
+ window.setTimeout(() => {
+ if (!opened()) return
+ if (terminal.active() !== id) return
+ focusTerminalById(id)
+ }, ms),
+ )
+
+ return () => {
+ cancelAnimationFrame(frame)
+ for (const timer of timers) clearTimeout(timer)
+ }
+ }
+
+ createEffect(
+ on(
+ () => [opened(), terminal.active()] as const,
+ ([next, id]) => {
+ if (!next || !id) return
+ const stop = focus(id)
+ onCleanup(stop)
+ },
+ ),
+ )
+
+ createEffect(() => {
+ if (opened()) return
+ const active = document.activeElement
+ if (!(active instanceof HTMLElement)) return
+ if (!root?.contains(active)) return
+ active.blur()
+ })
+
+ createEffect(() => {
+ const dir = sdk().directory
+ if (!dir) return
+ if (!terminal.ready()) return
+ language.locale()
+
+ setTerminalHandoff(
+ workspaceKey(),
+ terminal.all().map((pty) =>
+ terminalTabLabel({
+ title: pty.title,
+ titleNumber: pty.titleNumber,
+ t: language.t as (key: string, vars?: Record) => string,
+ }),
+ ),
+ )
+ })
+
+ const handoff = createMemo(() => {
+ const dir = sdk().directory
+ if (!dir) return []
+ return getTerminalHandoff(workspaceKey()) ?? []
+ })
+
+ const all = terminal.all
+ const ids = createMemo(() => all().map((pty) => pty.id))
+
+ const recoverTerminal = (key: string, id: string, clone: (id: string) => Promise) => {
+ if (store.recovered[key]) return
+ setStore("recovered", key, true)
+ void clone(id)
+ }
+
+ const terminalRecoveryKey = (pty: { id: string; title: string; titleNumber: number }) => {
+ return String(pty.titleNumber || pty.title || pty.id)
+ }
+
+ const markTerminalConnected = (key: string, id: string, trim: (id: string) => void) => {
+ setStore("recovered", key, false)
+ trim(id)
+ }
+
+ const handleTerminalDragEnd = () => {
+ const activeId = terminal.active()
+ if (!activeId) return
+ requestAnimationFrame(() => {
+ if (terminal.active() !== activeId) return
+ focusTerminalById(activeId)
+ })
+ }
+
+ return (
+
+
+
+ )
+}
diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css
index 036533c10fb8..fe450da85a5e 100644
--- a/packages/ui/src/components/tabs.css
+++ b/packages/ui/src/components/tabs.css
@@ -135,7 +135,8 @@
}
}
- #review-panel &[data-variant="normal"][data-orientation="horizontal"] {
+ #review-panel &[data-variant="normal"][data-orientation="horizontal"],
+ #terminal-panel &[data-variant="normal"][data-orientation="horizontal"] {
background-color: var(--background-stronger);
[data-slot="tabs-list"] {
@@ -267,6 +268,51 @@
}
}
+ #terminal-panel &[data-variant="normal"][data-orientation="horizontal"] {
+ [data-slot="tabs-list"] {
+ height: 52px;
+ padding-right: 12px;
+ gap: 8px;
+ }
+
+ [data-slot="tabs-trigger-wrapper"] {
+ height: 28px;
+ padding-inline: 8px;
+ border: 0;
+ background-color: transparent;
+ box-shadow: none;
+
+ &::after {
+ display: none;
+ }
+
+ [data-slot="tabs-trigger"] {
+ padding: 0 !important;
+ }
+
+ &:has([data-slot="tabs-trigger-close-button"]) {
+ padding-right: 8px;
+ }
+
+ &:has([data-selected]) {
+ background-color: var(--v2-background-bg-layer-01);
+ box-shadow: inset 0 0 0 0.5px var(--v2-border-border-muted);
+ }
+ }
+
+ [data-slot="terminal-tab-title"] {
+ font-family: Inter, sans-serif;
+ font-style: normal;
+ font-weight: 440;
+ font-size: 13px;
+ line-height: 16px;
+ letter-spacing: -0.04px;
+ color: var(--v2-text-text-base);
+ font-variation-settings: "slnt" 0;
+ font-variant-numeric: tabular-nums;
+ }
+ }
+
&[data-variant="alt"] {
[data-slot="tabs-list"] {
padding-left: 24px;