@@ -41,6 +41,12 @@ const PREVIEW_MODE_ICONS = {
4141 preview : Pencil ,
4242} satisfies Record < PreviewMode , ( props : ComponentProps < typeof Eye > ) => ReactNode >
4343
44+ const PREVIEW_MODE_LABELS : Record < PreviewMode , string > = {
45+ editor : 'Split Mode' ,
46+ split : 'Preview Mode' ,
47+ preview : 'Edit Mode' ,
48+ }
49+
4450/**
4551 * Builds a `type:id` -> current name lookup from live query data so resource
4652 * tabs always reflect the latest name even after a rename.
@@ -273,103 +279,105 @@ export function ResourceTabs({
273279 < p > Collapse</ p >
274280 </ Tooltip . Content >
275281 </ Tooltip . Root >
276- < div
277- ref = { scrollNodeRef }
278- className = { cn (
279- 'flex min-w-0 flex-1 items-center overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden' ,
280- RESOURCE_TAB_GAP_CLASS
281- ) }
282- onDragOver = { ( e ) => {
283- e . preventDefault ( )
284- startEdgeScroll ( e . clientX )
285- } }
286- onDrop = { handleDrop }
287- >
288- { resources . map ( ( resource , idx ) => {
289- const config = getResourceConfig ( resource . type )
290- const displayName = nameLookup . get ( `${ resource . type } :${ resource . id } ` ) ?? resource . title
291- const isActive = activeId === resource . id
292- const isHovered = hoveredTabId === resource . id
293- const isDragging = draggedIdx === idx
294- const showGapBefore =
295- dropGapIdx === idx &&
296- draggedIdx !== null &&
297- draggedIdx !== idx &&
298- draggedIdx !== idx - 1
299- const showGapAfter =
300- idx === resources . length - 1 &&
301- dropGapIdx === resources . length &&
302- draggedIdx !== null &&
303- draggedIdx !== idx
282+ < div className = { cn ( 'flex min-w-0 flex-1 items-center' , RESOURCE_TAB_GAP_CLASS ) } >
283+ < div
284+ ref = { scrollNodeRef }
285+ className = { cn (
286+ 'flex min-w-0 items-center overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden' ,
287+ RESOURCE_TAB_GAP_CLASS
288+ ) }
289+ onDragOver = { ( e ) => {
290+ e . preventDefault ( )
291+ startEdgeScroll ( e . clientX )
292+ } }
293+ onDrop = { handleDrop }
294+ >
295+ { resources . map ( ( resource , idx ) => {
296+ const config = getResourceConfig ( resource . type )
297+ const displayName = nameLookup . get ( `${ resource . type } :${ resource . id } ` ) ?? resource . title
298+ const isActive = activeId === resource . id
299+ const isHovered = hoveredTabId === resource . id
300+ const isDragging = draggedIdx === idx
301+ const showGapBefore =
302+ dropGapIdx === idx &&
303+ draggedIdx !== null &&
304+ draggedIdx !== idx &&
305+ draggedIdx !== idx - 1
306+ const showGapAfter =
307+ idx === resources . length - 1 &&
308+ dropGapIdx === resources . length &&
309+ draggedIdx !== null &&
310+ draggedIdx !== idx
304311
305- return (
306- < div key = { resource . id } className = 'relative flex shrink-0 items-center' >
307- { showGapBefore && (
308- < div className = '-translate-x-1/2 -translate-y-1/2 pointer-events-none absolute top-1/2 left-0 z-10 h-[16px] w-[2px] rounded-full bg-[var(--text-subtle)]' />
309- ) }
310- < Tooltip . Root >
311- < Tooltip . Trigger asChild >
312- < Button
313- variant = 'subtle'
314- draggable
315- onDragStart = { ( e ) => handleDragStart ( e , idx ) }
316- onDragOver = { ( e ) => handleDragOver ( e , idx ) }
317- onDragLeave = { handleDragLeave }
318- onDragEnd = { handleDragEnd }
319- onMouseDown = { ( e ) => {
320- if ( e . button === 1 && chatId ) {
321- e . preventDefault ( )
322- handleRemove ( e , resource )
323- }
324- } }
325- onClick = { ( ) => onSelect ( resource . id ) }
326- onMouseEnter = { ( ) => setHoveredTabId ( resource . id ) }
327- onMouseLeave = { ( ) => setHoveredTabId ( null ) }
328- className = { cn (
329- 'group relative shrink-0 bg-transparent px-[8px] py-[4px] pr-[22px] text-[12px] transition-opacity duration-150' ,
330- isActive && 'bg-[var(--surface-4)]' ,
331- isDragging && 'opacity-30'
332- ) }
333- >
334- { config . renderTabIcon ( resource , 'mr-[6px] h-[14px] w-[14px]' ) }
335- { displayName }
336- { ( isHovered || isActive ) && chatId && (
337- < span
338- role = 'button'
339- tabIndex = { - 1 }
340- onClick = { ( e ) => handleRemove ( e , resource ) }
341- onKeyDown = { ( e ) => {
342- if ( e . key === 'Enter' )
343- handleRemove ( e as unknown as React . MouseEvent , resource )
344- } }
345- className = '-translate-y-1/2 absolute top-1/2 right-[4px] flex items-center justify-center rounded-[4px] p-[1px] hover:bg-[var(--surface-5)]'
346- aria-label = { `Close ${ displayName } ` }
347- >
348- < svg
349- className = 'h-[10px] w-[10px] text-[var(--text-icon)]'
350- viewBox = '0 0 24 24'
351- fill = 'none'
352- stroke = 'currentColor'
353- strokeWidth = '2.5'
354- strokeLinecap = 'round'
355- strokeLinejoin = 'round'
312+ return (
313+ < div key = { resource . id } className = 'relative flex shrink-0 items-center' >
314+ { showGapBefore && (
315+ < div className = '-translate-x-1/2 -translate-y-1/2 pointer-events-none absolute top-1/2 left-0 z-10 h-[16px] w-[2px] rounded-full bg-[var(--text-subtle)]' />
316+ ) }
317+ < Tooltip . Root >
318+ < Tooltip . Trigger asChild >
319+ < Button
320+ variant = 'subtle'
321+ draggable
322+ onDragStart = { ( e ) => handleDragStart ( e , idx ) }
323+ onDragOver = { ( e ) => handleDragOver ( e , idx ) }
324+ onDragLeave = { handleDragLeave }
325+ onDragEnd = { handleDragEnd }
326+ onMouseDown = { ( e ) => {
327+ if ( e . button === 1 && chatId ) {
328+ e . preventDefault ( )
329+ handleRemove ( e , resource )
330+ }
331+ } }
332+ onClick = { ( ) => onSelect ( resource . id ) }
333+ onMouseEnter = { ( ) => setHoveredTabId ( resource . id ) }
334+ onMouseLeave = { ( ) => setHoveredTabId ( null ) }
335+ className = { cn (
336+ 'group relative shrink-0 bg-transparent px-[8px] py-[4px] pr-[22px] text-[12px] transition-opacity duration-150' ,
337+ isActive && 'bg-[var(--surface-4)]' ,
338+ isDragging && 'opacity-30'
339+ ) }
340+ >
341+ { config . renderTabIcon ( resource , 'mr-[6px] h-[14px] w-[14px]' ) }
342+ { displayName }
343+ { ( isHovered || isActive ) && chatId && (
344+ < span
345+ role = 'button'
346+ tabIndex = { - 1 }
347+ onClick = { ( e ) => handleRemove ( e , resource ) }
348+ onKeyDown = { ( e ) => {
349+ if ( e . key === 'Enter' )
350+ handleRemove ( e as unknown as React . MouseEvent , resource )
351+ } }
352+ className = '-translate-y-1/2 absolute top-1/2 right-[4px] flex items-center justify-center rounded-[4px] p-[1px] hover:bg-[var(--surface-5)]'
353+ aria-label = { `Close ${ displayName } ` }
356354 >
357- < path d = 'M18 6 6 18M6 6l12 12' />
358- </ svg >
359- </ span >
360- ) }
361- </ Button >
362- </ Tooltip . Trigger >
363- < Tooltip . Content side = 'bottom' >
364- < p > { displayName } </ p >
365- </ Tooltip . Content >
366- </ Tooltip . Root >
367- { showGapAfter && (
368- < div className = '-translate-y-1/2 pointer-events-none absolute top-1/2 right-0 z-10 h-[16px] w-[2px] translate-x-1/2 rounded-full bg-[var(--text-subtle)]' />
369- ) }
370- </ div >
371- )
372- } ) }
355+ < svg
356+ className = 'h-[10px] w-[10px] text-[var(--text-icon)]'
357+ viewBox = '0 0 24 24'
358+ fill = 'none'
359+ stroke = 'currentColor'
360+ strokeWidth = '2.5'
361+ strokeLinecap = 'round'
362+ strokeLinejoin = 'round'
363+ >
364+ < path d = 'M18 6 6 18M6 6l12 12' />
365+ </ svg >
366+ </ span >
367+ ) }
368+ </ Button >
369+ </ Tooltip . Trigger >
370+ < Tooltip . Content side = 'bottom' >
371+ < p > { displayName } </ p >
372+ </ Tooltip . Content >
373+ </ Tooltip . Root >
374+ { showGapAfter && (
375+ < div className = '-translate-y-1/2 pointer-events-none absolute top-1/2 right-0 z-10 h-[16px] w-[2px] translate-x-1/2 rounded-full bg-[var(--text-subtle)]' />
376+ ) }
377+ </ div >
378+ )
379+ } ) }
380+ </ div >
373381 { chatId && (
374382 < AddResourceDropdown
375383 workspaceId = { workspaceId }
@@ -395,7 +403,7 @@ export function ResourceTabs({
395403 </ Button >
396404 </ Tooltip . Trigger >
397405 < Tooltip . Content side = 'bottom' >
398- < p > Preview mode </ p >
406+ < p > { PREVIEW_MODE_LABELS [ previewMode ] } </ p >
399407 </ Tooltip . Content >
400408 </ Tooltip . Root >
401409 ) }
0 commit comments