Skip to main content
The OpenFiles component displays a horizontal tab bar showing all currently open files, allowing users to switch between files and close tabs.

Usage

import { OpenFiles } from '@/components/open-files';

function Editor() {
  const { openFiles, activeFilePath, setActiveFilePath, handleCloseTab, currentTheme } = useEditor();
  
  return (
    <div>
      <OpenFiles
        openFiles={openFiles}
        activeFilePath={activeFilePath}
        onTabClick={setActiveFilePath}
        onCloseTab={handleCloseTab}
        currentTheme={currentTheme}
      />
      <EditorView />
    </div>
  );
}

Props

openFiles
OpenFile[]
required
Array of currently open files
activeFilePath
string | null
required
Path of the currently active/focused file
onTabClick
(path: string) => void
required
Callback fired when a tab is clicked to switch files
onCloseTab
(path: string) => void
required
Callback fired when the close button (X) is clicked on a tab
currentTheme
ThemeConfig
required
Current theme configuration for styling tabs

Features

Tab Display

  • Shows file name for each open file
  • Visual indicator (●) for modified/unsaved files
  • Close button (X icon) on each tab
  • Horizontal scrollable layout

Visual States

  • Active tab: Transparent background
  • Inactive tab: Uses currentTheme.openFilePill background
  • Modified file: Blue dot indicator (●) next to filename
  • Hover effects: Reduced opacity on hover (0.5)

Interaction

  • Click tab to switch to that file
  • Click X icon to close tab (stops propagation to prevent switching)
  • Whitespace handling with whitespace-nowrap

OpenFile Type

interface OpenFile {
  path: string;      // Full file path
  name: string;      // File name only
  content: string;   // File contents
  modified: boolean; // Unsaved changes flag
}

Implementation

src/components/open-files.tsx
import { OpenFile, ThemeConfig } from "@/lib/types";
import { XIcon } from "lucide-react";

export function OpenFiles(props: {
  openFiles: OpenFile[];
  onTabClick: (path: string) => void;
  onCloseTab: (path: string) => void;
  currentTheme: ThemeConfig;
  activeFilePath: string | null;
}) {
  return (
    <>
      {props.openFiles.length > 0 && (
        <div className="flex gap-2 h-[25px]">
          {props.openFiles.map((file) => (
            <div
              onClick={() => props.onTabClick(file.path)}
              key={file.path}
              className={`flex items-center gap-2 text-[12px] rounded-md px-3 cursor-pointer hover:opacity-50`}
              style={{
                backgroundColor:
                  file.path === props.activeFilePath
                    ? "var(--colors-transparent)"
                    : props.currentTheme.openFilePill?.bg,
                color: props.currentTheme.openFilePill?.fg,
              }}
            >
              <p className="whitespace-nowrap">
                {file.name}
                {file.modified && <span className="text-[#569cd6]"></span>}
              </p>
              <span
                className="opacity-60 hover:opacity-100"
                onClick={(e) => {
                  e.stopPropagation();
                  props.onCloseTab(file.path);
                }}
              >
                <XIcon size={12} />
              </span>
            </div>
          ))}
        </div>
      )}
    </>
  );
}

Styling

  • Tab height: 25px fixed height
  • Font size: 12px
  • Spacing: 2px gap between tabs, 3px horizontal padding
  • Border radius: Rounded corners with rounded-md
  • Icons: 12px X icon from lucide-react

Theme Integration

Tabs use the openFilePill theme property:
{
  openFilePill: {
    bg: "#252526",  // Background for inactive tabs
    fg: "#cccccc"   // Text color
  }
}

Usage in Editor Component

The OpenFiles component is integrated at the top of the Editor:
src/components/Editor.tsx
return (
  <div className="w-[calc(100vw-240px)] space-y-2 py-2 pb-0 pr-2 h-screen flex flex-col">
    <OpenFiles
      openFiles={openFiles}
      onTabClick={setActiveFilePath}
      onCloseTab={handleCloseTab}
      currentTheme={currentTheme}
      activeFilePath={activeFilePath}
    />
    <div ref={editorRef} className="h-full rounded-tl-2xl overflow-hidden" />
  </div>
);

Build docs developers (and LLMs) love