+ )
+}
diff --git a/packages/app/src/components/titlebar-tab-strip.tsx b/packages/app/src/components/titlebar-tab-strip.tsx
new file mode 100644
index 00000000000..c5e39a26887
--- /dev/null
+++ b/packages/app/src/components/titlebar-tab-strip.tsx
@@ -0,0 +1,581 @@
+import {
+ createEffect,
+ createMemo,
+ createResource,
+ createRoot,
+ createSignal,
+ For,
+ onCleanup,
+ onMount,
+ Show,
+} from "solid-js"
+import { Portal } from "solid-js/web"
+import { createStore } from "solid-js/store"
+import { makeEventListener } from "@solid-primitives/event-listener"
+import { createResizeObserver } from "@solid-primitives/resize-observer"
+import { tabHref, tabKey, type SessionTab, type Tab } from "@/context/tabs"
+import { ServerConnection } from "@/context/server"
+import { DraftTabItem, TabNavItem } from "@/components/titlebar-tab-nav"
+import { useGlobal, type ServerCtx } from "@/context/global"
+import { useLanguage } from "@/context/language"
+import { useCommand } from "@/context/command"
+import { useTabs } from "@/context/tabs"
+import { createTabPromptState } from "@/context/prompt"
+import { base64Encode } from "@opencode-ai/core/util/encode"
+import {
+ captureTabPointerDown,
+ canStartTabDrag,
+ createTabDragPreview,
+ isPrimaryPointerPressed,
+ isTabCloseTarget,
+} from "./titlebar-tab-gesture"
+import {
+ ACTIVATION_DISTANCE,
+ autoscrollSpeed,
+ captureTabDragLayout,
+ clampFloaterLeft,
+ draftOrderChanged,
+ insertIndexFromVirtualLayout,
+ movePlaceholder,
+ pointerDistance,
+ syncLayoutScroll,
+ type TabDragLayout,
+} from "@/components/titlebar-tab-drag"
+
+function SessionTabSlot(props: {
+ tab: SessionTab
+ id: string
+ first: () => boolean
+ active: () => boolean
+ activeServerKey: ServerConnection.Key
+ forceTruncate: boolean
+ dragActive: boolean
+ dragged: () => boolean
+ pressed: () => boolean
+ serverCtx: () => ServerCtx | undefined
+ suppressNavigation: () => boolean
+ onPointerDown: (event: PointerEvent) => void
+ onNavigate: (element: HTMLDivElement) => void
+ onClose: () => void
+}) {
+ const tabs = useTabs()
+ let ref!: HTMLDivElement
+ const sdk = createMemo(() => props.serverCtx()?.sdk ?? null)
+ const cachedSession = createMemo(() => props.serverCtx()?.sync.session.peek(props.tab.sessionId))
+ const [loadedSession] = createResource(
+ () => {
+ const ctx = props.serverCtx()
+ return ctx ? { id: props.tab.sessionId, ctx } : null
+ },
+ ({ id, ctx }) => ctx.sync.session.resolve(id).catch(() => undefined),
+ )
+ const session = createMemo(() => cachedSession() ?? loadedSession())
+ let prefetched = false
+
+ createEffect(() => {
+ const ctx = props.serverCtx()
+ const value = session()
+ if (!ctx || !value || prefetched) return
+ prefetched = true
+ createRoot((dispose) => {
+ try {
+ void ctx.sync
+ .ensureDirSyncContext(value.directory)
+ .session.sync(value.id)
+ .catch(() => {})
+ .finally(dispose)
+ } catch {
+ dispose()
+ }
+ })
+ })
+
+ createEffect(() => {
+ const value = session()
+ const current = sdk()
+ if (!value || !current) return
+ createTabPromptState(tabs, props.tab, current.scope, {
+ dir: base64Encode(value.directory),
+ id: value.id,
+ })
+ })
+
+ return (
+