فهرست منبع

fix(app): restore tab layout consistency (#33804)

Brendan Allan 1 ماه پیش
والد
کامیت
b44af11e03

+ 22 - 0
packages/app/src/components/titlebar-tab-drag.test.ts

@@ -56,6 +56,28 @@ describe("titlebar tab drag", () => {
     expect(captureTabDragLayout(list, ["a", "b"]).dividerWidth).toBe(13)
     list.remove()
   })
+
+  test("uses the list gap as the divider width", () => {
+    const list = document.createElement("div")
+    const first = document.createElement("div")
+    const second = document.createElement("div")
+    const firstTab = document.createElement("div")
+    const secondTab = document.createElement("div")
+    first.dataset.titlebarTabSlot = ""
+    first.dataset.tabKey = "a"
+    second.dataset.titlebarTabSlot = ""
+    second.dataset.tabKey = "b"
+    firstTab.dataset.titlebarTab = ""
+    secondTab.dataset.titlebarTab = ""
+    first.append(firstTab)
+    second.append(secondTab)
+    list.append(first, second)
+    list.style.columnGap = "13.5px"
+    document.body.append(list)
+
+    expect(captureTabDragLayout(list, ["a", "b"]).dividerWidth).toBe(13.5)
+    list.remove()
+  })
 })
 
 describe("titlebar tab gestures", () => {

+ 5 - 3
packages/app/src/components/titlebar-tab-drag.ts

@@ -29,6 +29,7 @@ export function captureTabDragLayout(list: HTMLElement, order: string[]) {
 
   let dividerWidth = 0
   if (order.length >= 2) {
+    const gap = Number.parseFloat(getComputedStyle(list).columnGap) || 0
     const secondId = order[1]
     for (const slot of slots) {
       if (slot.dataset.tabKey !== secondId) continue
@@ -36,10 +37,11 @@ export function captureTabDragLayout(list: HTMLElement, order: string[]) {
       if (!tab) break
       const style = getComputedStyle(slot)
       dividerWidth =
+        gap ||
         slot.getBoundingClientRect().width -
-        tab.getBoundingClientRect().width +
-        (Number.parseFloat(style.marginLeft) || 0) +
-        (Number.parseFloat(style.marginRight) || 0)
+          tab.getBoundingClientRect().width +
+          (Number.parseFloat(style.marginLeft) || 0) +
+          (Number.parseFloat(style.marginRight) || 0)
       break
     }
   }

+ 33 - 4
packages/app/src/components/titlebar-tab-nav.css

@@ -9,6 +9,29 @@
   justify-content: center;
 }
 
+[data-slot="titlebar-tabs"] {
+  container-type: inline-size;
+}
+
+[data-titlebar-tab-list] {
+  gap: calc(min(12px, 2cqw) + 1.5px);
+}
+
+[data-titlebar-tab-slot] {
+  position: relative;
+}
+
+[data-titlebar-tab-slot]:not(:first-child)::before {
+  content: "";
+  position: absolute;
+  top: 8px;
+  left: calc(0px - min(6px, 1cqw) - 0.75px);
+  width: 1.5px;
+  height: 12px;
+  border-radius: 9999px;
+  background: var(--v2-background-bg-layer-02);
+}
+
 [data-titlebar-tab] [data-slot="tab-close"]::before {
   content: "";
   position: absolute;
@@ -62,8 +85,14 @@
   display: none;
 }
 
-[data-titlebar-tab]:hover [data-slot="tab-close"],
-[data-titlebar-tab][data-active="true"] [data-slot="tab-close"],
-[data-titlebar-tab][data-editing="true"] [data-slot="tab-close"] {
-  background: var(--tab-bg);
+@container (max-width: 64px) {
+  [data-titlebar-tab-link] {
+    justify-content: center;
+    gap: 0;
+    padding-inline: 0;
+  }
+
+  [data-titlebar-tab-title] {
+    display: none;
+  }
 }

+ 8 - 3
packages/app/src/components/titlebar-tab-nav.tsx

@@ -178,7 +178,7 @@ export function TabNavItem(props: {
       data-slot="titlebar-tab-item"
       data-title-overflow={titleOverflowing()}
       data-editing={editing()}
-      class="group relative flex h-7 min-w-24 max-w-60 select-none flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] px-1.5 [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[editing='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
+      class="group relative flex h-7 w-full min-w-0 max-w-56 select-none flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] px-1.5 [container-type:inline-size] [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[editing='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
       classList={{ invisible: props.hidden }}
       data-active={props.active}
       data-dragging={props.dragging}
@@ -193,6 +193,7 @@ export function TabNavItem(props: {
           return (
             <a
               data-slot="tab-link"
+              data-titlebar-tab-link
               href={props.href}
               draggable={false}
               onDragStart={(event) => {
@@ -221,6 +222,7 @@ export function TabNavItem(props: {
                   titleEl.textContent = session().title
                 }}
                 data-slot="tab-title"
+                data-titlebar-tab-title
                 class="min-w-0 flex-1 outline-none leading-4"
                 classList={{
                   "overflow-hidden text-clip whitespace-nowrap": !editing(),
@@ -297,7 +299,7 @@ export function DraftTabItem(props: {
       data-active={props.active}
       data-dragging={props.dragging}
       data-pressed={props.pressed}
-      class="group relative shrink-0 flex h-7 max-w-60 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--tab-bg)] pl-1.5 pr-8 whitespace-nowrap [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-overlay-simple-overlay-pressed)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
+      class="group relative flex h-7 w-full min-w-0 max-w-56 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--tab-bg)] pl-1.5 pr-8 [container-type:inline-size] whitespace-nowrap [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-overlay-simple-overlay-pressed)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true'][data-pressed='true']:[--tab-bg:var(--v2-overlay-simple-overlay-pressed)]"
       classList={{ invisible: props.hidden }}
       onMouseDown={(event) => {
         if (event.button !== 1) return
@@ -306,6 +308,7 @@ export function DraftTabItem(props: {
     >
       <a
         data-slot="tab-link"
+        data-titlebar-tab-link
         href={props.href}
         draggable={false}
         onDragStart={(event) => {
@@ -322,7 +325,9 @@ export function DraftTabItem(props: {
         <span class="flex size-4 shrink-0 items-center justify-center">
           <IconV2 name="edit" />
         </span>
-        <span class="truncate leading-5">{props.title}</span>
+        <span data-titlebar-tab-title class="truncate leading-5">
+          {props.title}
+        </span>
       </a>
       <div data-slot="tab-close" class="absolute right-0 inset-y-0 flex w-7 items-center justify-center">
         <IconButtonV2

+ 4 - 9
packages/app/src/components/titlebar-tab-strip.tsx

@@ -45,7 +45,6 @@ import {
 function SessionTabSlot(props: {
   tab: SessionTab
   id: string
-  first: () => boolean
   active: () => boolean
   activeServerKey: ServerConnection.Key
   forceTruncate: boolean
@@ -104,10 +103,9 @@ function SessionTabSlot(props: {
     <div
       data-titlebar-tab-slot
       data-tab-key={props.id}
-      class="flex shrink-0"
+      class="flex min-w-0 max-w-56 flex-1 basis-0"
       classList={{
         hidden: !session(),
-        "ml-1.5 border-l border-[var(--v2-background-bg-layer-02)] pl-1.5": !props.first(),
         "pointer-events-none": props.dragActive,
       }}
       onPointerDown={props.onPointerDown}
@@ -461,17 +459,16 @@ export function TitlebarTabStrip(props: {
 
   return (
     <>
-      <div data-slot="titlebar-tabs" class="relative min-w-0">
+      <div data-slot="titlebar-tabs" class="relative min-w-0 flex-1">
         <div
           data-slot="titlebar-tabs-scroll"
           class="flex min-w-0 flex-row items-center gap-1.5 overflow-x-auto no-scrollbar [app-region:no-drag]"
           ref={scrollRef}
         >
-          <div class="flex min-w-0 flex-row items-center" ref={listRef}>
+          <div data-titlebar-tab-list class="flex w-full min-w-0 flex-row items-center" ref={listRef}>
             <For each={displayTabs()}>
               {(tab, index) => {
                 const id = tabKey(tab)
-                const first = () => index() === 0
                 let ref!: HTMLDivElement
                 useTabShortcut(index, () => props.onNavigate(tab, ref))
 
@@ -487,7 +484,6 @@ export function TitlebarTabStrip(props: {
                     <SessionTabSlot
                       tab={tab}
                       id={id}
-                      first={first}
                       active={() => props.currentTab() === tab}
                       activeServerKey={props.activeServerKey}
                       forceTruncate={props.forceTruncate}
@@ -510,9 +506,8 @@ export function TitlebarTabStrip(props: {
                   <div
                     data-titlebar-tab-slot
                     data-tab-key={id}
-                    class="flex shrink-0"
+                    class="flex min-w-0 max-w-56 flex-1 basis-0"
                     classList={{
-                      "ml-1.5 border-l border-[var(--v2-background-bg-layer-02)] pl-1.5": !first(),
                       "pointer-events-none": drag.active,
                     }}
                     onPointerDown={(event) => {

+ 0 - 1
packages/app/src/components/titlebar.tsx

@@ -481,7 +481,6 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
                     />
                   </TooltipV2>
                 </Show>
-                <div class="flex-1" />
                 <TitlebarV2Right state={v2RightState()} />
                 <Show when={windows() && !electronWindows()}>
                   <div data-tauri-decorum-tb class="flex flex-row" />

+ 1 - 1
packages/app/src/pages/session/composer/session-composer-region.tsx

@@ -252,7 +252,7 @@ export function SessionComposerRegion(props: {
             </Show>
             <div
               classList={{
-                "relative z-10": true,
+                "relative z-30": true,
               }}
               style={{
                 "margin-top": `${-lift()}px`,