@@ -181,6 +181,8 @@ interface TabProps {
181181 dropPosition ?: "left" | "right" | null ;
182182 isFirstTab ?: boolean ;
183183 isLastTab ?: boolean ;
184+ isBeforeActive ?: boolean ;
185+ isAfterActive ?: boolean ;
184186 /** Tab decorations from diagnostics/git */
185187 decoration ?: TabDecoration ;
186188}
@@ -251,48 +253,66 @@ function Tab(props: TabProps) {
251253 if ( props . dropPosition === "right" ) classes . push ( "drop-target-right" ) ;
252254 if ( props . isFirstTab ) classes . push ( "first-tab" ) ;
253255 if ( props . isLastTab ) classes . push ( "last-tab" ) ;
256+ if ( props . isBeforeActive ) classes . push ( "before-active" ) ;
257+ if ( props . isAfterActive ) classes . push ( "after-active" ) ;
254258
255259 // Icon class for padding adjustment
256260 classes . push ( "has-icon" , "tab-actions-right" ) ;
257261
258262 return classes . join ( " " ) ;
259263 } ) ;
260264
261- // Tab dimensions based on design specs - Trae Dark Minimal Floating Card Style
265+ // Tab dimensions based on Figma design specs
266+ // Active tab: #1A1A1A background, merged with editor (no bottom border)
267+ // Inactive tabs: transparent background, muted text color
262268 const getTabStyle = ( ) => {
263269 const baseStyle : Record < string , string > = {
264270 "box-sizing" : "border-box" ,
265- padding : "0 14px " ,
271+ padding : "0 16px " ,
266272 "outline-offset" : "-2px" ,
267- border : "none" , // NO borders in Trae style
273+ border : "none" ,
274+ display : "flex" ,
275+ "align-items" : "center" ,
276+ gap : "8px" ,
268277 } ;
269278
270279 // Apply decoration color override
271280 const dec = decoration ( ) ;
272281 const decorationColor = dec . color ;
273282
274- // Trae Dark Minimal: Active tab merges with editor
275- // JetBrains compact tabs: 28px height
283+ // Figma design: Active tab merges seamlessly with editor (#1A1A1A)
276284 if ( props . isActive ) {
277- baseStyle . height = "28px" ;
278- // Rounded top corners (6px), flat bottom to merge with editor
279- baseStyle [ "border-radius" ] = "6px 6px 0 0" ;
280- baseStyle . background = tokens . colors . surface . canvas ;
281- baseStyle . color = decorationColor ?? tokens . colors . text . primary ;
282- // Subtle shadow for floating card effect
283- baseStyle [ "box-shadow" ] = "0 -1px 3px var(--jb-shadow-color, rgba(0, 0, 0, 0.15))" ;
285+ baseStyle . height = "100%" ;
286+ baseStyle [ "border-radius" ] = "0" ;
287+ // Same background as editor panel - seamless merge
288+ baseStyle . background = "var(--figma-bg-secondary, #1A1A1A)" ;
289+ baseStyle . color = decorationColor ?? "var(--figma-text-primary, #FFFFFF)" ;
290+ baseStyle [ "box-shadow" ] = "none" ;
284291 baseStyle [ "z-index" ] = "10" ;
292+ // Active tab: NO border, merges with editor
293+ baseStyle . border = "none" ;
285294 } else {
286- // Inactive tabs are same height with muted text
287- baseStyle . height = "28px" ;
288- baseStyle [ "border-radius" ] = "4px 4px 0 0" ;
289- baseStyle . background = isHovered ( ) ? tokens . colors . interactive . hover : "transparent" ;
290- // Apply decoration color or default colors
295+ // Inactive tabs: #131217 background, muted text
296+ baseStyle . height = "100%" ;
297+ baseStyle [ "border-radius" ] = "0" ;
298+ baseStyle . background = isHovered ( )
299+ ? "rgba(255, 255, 255, 0.06)"
300+ : "#131217" ;
301+ // Figma: subtle border - only right border to avoid double borders
302+ baseStyle [ "border-top" ] = "1px solid rgba(255, 255, 255, 0.15)" ;
303+ baseStyle [ "border-bottom" ] = "1px solid rgba(255, 255, 255, 0.15)" ;
304+ baseStyle [ "border-right" ] = "1px solid rgba(255, 255, 255, 0.15)" ;
305+ baseStyle [ "border-left" ] = "none" ;
291306 if ( decorationColor ) {
292307 baseStyle . color = decorationColor ;
293308 } else {
294- baseStyle . color = isHovered ( ) ? tokens . colors . text . primary : tokens . colors . text . muted ;
309+ // Figma design: inactive tabs have more muted text (#666 vs #808080)
310+ baseStyle . color = isHovered ( )
311+ ? "var(--figma-text-primary, #FFFFFF)"
312+ : "rgba(255, 255, 255, 0.8)" ;
295313 }
314+ // Inactive tabs slightly dimmed per Figma design
315+ baseStyle . opacity = isHovered ( ) ? "1" : "0.85" ;
296316 }
297317
298318 // Sticky compact = 38px × 38px
@@ -311,7 +331,7 @@ function Tab(props: TabProps) {
311331 // Fit mode = minimum space needed (80px min, 200px max)
312332 else if ( sizingMode ( ) === "fit" ) {
313333 baseStyle [ "min-width" ] = "80px" ;
314- baseStyle [ "max-width" ] = "200px " ;
334+ baseStyle [ "max-width" ] = "fit-content " ;
315335 baseStyle [ "flex-shrink" ] = "0" ;
316336 baseStyle [ "flex-grow" ] = "0" ;
317337 baseStyle . width = "auto" ;
@@ -432,12 +452,10 @@ function Tab(props: TabProps) {
432452
433453 { /* Tab content - 8px gap for proper icon-to-text spacing */ }
434454 < div
435- class = "tab-label flex items-center min-w-0 w-full"
455+ class = "tab-label flex items-center w-full"
436456 style = { {
437457 "line-height" : "35px" ,
438- flex : "1" ,
439458 "white-space" : "nowrap" ,
440- overflow : "hidden" ,
441459 gap : "8px" , // Design spec: 8px spacing from text
442460 } }
443461 >
@@ -456,7 +474,7 @@ function Tab(props: TabProps) {
456474
457475 { /* File icon - 16px with 8px spacing (hidden in compact pinned mode) */ }
458476 < Show when = { ! ( props . isPinned && stickyMode ( ) === "compact" ) } >
459- < FileIcon name = { props . file . name } size = { 16 } />
477+ < FileIcon name = { props . file . name . split ( / [ / \\ \\ ] / ) . pop ( ) || props . file . name } size = { 16 } />
460478 </ Show >
461479
462480 { /* Pin icon indicator for pinned tabs (shown after file icon in non-compact mode) */ }
@@ -477,20 +495,22 @@ function Tab(props: TabProps) {
477495 { /* Preview tabs have italic title, deleted files have strikethrough */ }
478496 < Show when = { ! ( props . isPinned && stickyMode ( ) === "compact" ) } >
479497 < span
480- class = "truncate flex-1"
498+
481499 style = { {
482500 "font-size" : "var(--jb-font-size-sm)" ,
483501 "font-style" : ( props . isPreview || decoration ( ) . italic ) ? "italic" : "normal" ,
484502 "font-weight" : props . isActive ? "500" : "400" ,
485503 "text-decoration" : decoration ( ) . strikethrough ? "line-through" : "none" ,
486- // Use decoration color if available, otherwise default colors
504+ // Full filename display with ellipsis for very long names
487505 color : decoration ( ) . color ?? ( props . isActive
488506 ? tokens . colors . text . primary
489507 : tokens . colors . text . muted ) ,
490508 transition : "color 150ms ease-out, font-style 150ms ease-out, text-decoration 150ms ease-out" ,
509+
491510 } }
511+ title = { props . file . name . split ( / [ / \\ \\ ] / ) . pop ( ) || props . file . name }
492512 >
493- { props . file . name }
513+ { props . file . name . split ( / [ / \\ \\ ] / ) . pop ( ) || props . file . name }
494514 </ span >
495515 </ Show >
496516
@@ -955,16 +975,16 @@ export function TabBar(props: TabBarProps) {
955975
956976 return (
957977 < div
958- class = { `tabs-and-actions-container relative flex items-end shrink-0 ${ wrapTabs ( ) ? "wrapping" : "" } ` }
978+ class = { `tabs-and-actions-container relative flex items-stretch shrink-0 ${ wrapTabs ( ) ? "wrapping" : "" } ` }
959979 style = { {
960- // JetBrains compact: 28px tab bar height
961- height : wrapTabs ( ) ? "auto" : "28px " ,
962- "min-height" : "28px " ,
963- // Inherit from parent card for consistent surface color
964- background : "inherit " ,
980+ // Figma design: 47px tab bar height
981+ height : wrapTabs ( ) ? "auto" : "36px " ,
982+ "min-height" : "36px " ,
983+ // Figma: Dark background for tab bar, active tab merges with editor
984+ background : "var(--figma-bg-primary, #131217) " ,
965985 border : "none" ,
966986 // Minimal padding
967- padding : "0 2px " ,
987+ padding : "0" ,
968988 } }
969989 >
970990 { /* Scroll left button - hidden when tab wrapping is enabled */ }
@@ -973,8 +993,7 @@ export function TabBar(props: TabBarProps) {
973993 onClick = { scrollLeft }
974994 class = "w-6 flex items-center justify-center transition-colors z-10 rounded"
975995 style = { {
976- height : "28px" ,
977- color : tokens . colors . text . muted ,
996+ height : "100%" , color : tokens . colors . text . muted ,
978997 border : "none" ,
979998 "margin-right" : "2px" ,
980999 } }
@@ -989,7 +1008,7 @@ export function TabBar(props: TabBarProps) {
9891008 class = { `tabs-container flex-1 flex items-stretch no-scrollbar ${ wrapTabs ( ) ? "wrapping" : "overflow-x-auto" } ` }
9901009 style = { {
9911010 // JetBrains compact: 28px height
992- height : wrapTabs ( ) ? "auto" : "28px " ,
1011+ height : wrapTabs ( ) ? "auto" : "36px " ,
9931012 "scrollbar-width" : "none" , // Firefox
9941013 ...( wrapTabs ( ) ? { "flex-wrap" : "wrap" } : { } ) ,
9951014 } }
@@ -1003,9 +1022,18 @@ export function TabBar(props: TabBarProps) {
10031022 const isLast = index ( ) === sortedFiles ( ) . length - 1 ;
10041023 const isPinned = isTabPinned ( file . id ) ;
10051024
1025+ // Check if this tab is adjacent to the active tab
1026+ const files = sortedFiles ( ) ;
1027+ const currentActiveId = activeFileId ( ) ;
1028+ const activeIdx = files . findIndex ( f => f . id === currentActiveId ) ;
1029+ const currentIdx = index ( ) ;
1030+ const isBeforeActive = currentIdx === activeIdx - 1 && activeIdx > 0 ;
1031+ const isAfterActive = currentIdx === activeIdx + 1 && activeIdx < files . length - 1 ;
1032+
10061033 return (
10071034 < div
10081035 ref = { tabRef }
1036+ style = { { display : "contents" } }
10091037 onDragOver = { ( e ) => tabRef && handleDragOver ( e , file . id , tabRef ) }
10101038 onDragLeave = { handleDragLeave }
10111039 onDrop = { ( e ) => handleDrop ( e , file . id ) }
@@ -1041,6 +1069,8 @@ export function TabBar(props: TabBarProps) {
10411069 dropPosition = { state . overId === file . id ? state . position : null }
10421070 isFirstTab = { isFirst }
10431071 isLastTab = { isLast }
1072+ isBeforeActive = { isBeforeActive }
1073+ isAfterActive = { isAfterActive }
10441074 />
10451075 </ div >
10461076 ) ;
@@ -1061,8 +1091,7 @@ export function TabBar(props: TabBarProps) {
10611091 onClick = { scrollRight }
10621092 class = "w-6 flex items-center justify-center transition-colors z-10 rounded"
10631093 style = { {
1064- height : "28px" ,
1065- color : tokens . colors . text . muted ,
1094+ height : "100%" , color : tokens . colors . text . muted ,
10661095 border : "none" ,
10671096 "margin-left" : "2px" ,
10681097 } }
@@ -1085,8 +1114,7 @@ export function TabBar(props: TabBarProps) {
10851114 } }
10861115 class = "w-7 flex items-center justify-center transition-colors rounded"
10871116 style = { {
1088- height : "28px" ,
1089- color : tokens . colors . text . muted ,
1117+ height : "100%" , color : tokens . colors . text . muted ,
10901118 border : "none" ,
10911119 "margin-left" : tokens . spacing . sm ,
10921120 } }
@@ -1103,8 +1131,7 @@ export function TabBar(props: TabBarProps) {
11031131 onClick = { ( ) => setShowOverflow ( ! showOverflow ( ) ) }
11041132 class = "w-7 flex items-center justify-center transition-colors rounded"
11051133 style = { {
1106- height : "28px" ,
1107- color : tokens . colors . text . muted ,
1134+ height : "100%" , color : tokens . colors . text . muted ,
11081135 border : "none" ,
11091136 } }
11101137 title = "Show all tabs"
@@ -1132,8 +1159,7 @@ export function TabBar(props: TabBarProps) {
11321159 } }
11331160 class = "w-7 flex items-center justify-center transition-colors rounded"
11341161 style = { {
1135- height : "28px" ,
1136- color : tokens . colors . text . muted ,
1162+ height : "100%" , color : tokens . colors . text . muted ,
11371163 border : "none" ,
11381164 } }
11391165 title = "Close split"
0 commit comments