Skip to main content

Component Hierarchy

Blink’s UI is built with React components organized in a hierarchical structure:

Core Components

Editor Component

The main code editing component integrating Monaco Editor. Location: src/components/Editor/Editor.tsx
interface EditorProps {
    tabs: TabData[];
    activeTabIndex: number;
    setActiveTabIndex: (index: number) => void;
    onCloseTab: (index: number) => void;
    onContentChange: (newContent: string) => void;
    onCursorChange: (pos: { line: number, column: number }) => void;
    onValidationChange: (markers: any[]) => void;
    tree: TreeNode | null;
    openFolder: () => void;
    onOpenFile: (name: string, type: FileType, path: string) => void;
    // Settings props
    settings: EditorSettings;
    onSettingChange: (key: keyof EditorSettings, value: any) => void;
    isSettingsActive: boolean;
    showSettingsTab: boolean;
    onOpenSettings: () => void;
    onCloseSettings: () => void;
    onOpenSettingsFolder: () => void;
}
The Editor component maintains Monaco models in memory to preserve undo history and validation state when switching tabs.

Monaco Editor Configuration

// src/components/Editor/Editor.tsx:166-188
options={{
    fontSize: settings.fontSize,
    minimap: { enabled: settings.minimap },
    scrollBeyondLastLine: settings.scrollBeyondLastLine,
    automaticLayout: true,
    fontFamily: settings.fontFamily,
    lineHeight: settings.lineHeight,
    padding: { top: 20 },
    wordBasedSuggestions: 'allDocuments',
    suggestOnTriggerCharacters: true,
    acceptSuggestionOnEnter: 'on',
    tabCompletion: 'on',
    links: true,
    renderValidationDecorations: 'on',
    tabSize: settings.tabSize,
    wordWrap: settings.wordWrap,
    lineNumbers: settings.lineNumbers,
    cursorStyle: settings.cursorStyle,
    cursorBlinking: settings.cursorBlinking,
    fontLigatures: settings.fontLigatures,
    renderWhitespace: settings.renderWhitespace,
    bracketPairColorization: { enabled: settings.bracketPairColorization },
}}

Explorer Component

File tree sidebar with drag-and-drop support. Location: src/components/Explorer/Explorer.tsx
interface ExplorerProps {
    onFileClick: (name: string, type: FileType, path: string) => void;
    tree: any;
    onNewFile: () => void;
    onNewFolder: () => void;
    selectedPath: string | null;
    onSelect: (path: string) => void;
    onExpand: (path: string) => void;
    onContextMenu: (e: React.MouseEvent, node: any) => void;
    onMove: (srcPath: string, targetPath: string) => void;
}

Resize Handling

// src/components/Explorer/Explorer.tsx:47-54
const handleMouseMove = useCallback((e: MouseEvent) => {
    if (!isResizing.current) return;
    
    // Calculate new width based on sidebar offset (64px)
    const newWidth = e.clientX - 64; 
    if (newWidth > 150 && newWidth < 600) {
        setWidth(newWidth);
    }
}, []);

TerminalPanel Component

Integrated terminal using xterm.js and node-pty. Location: src/components/BottomBar/TerminalPanel.tsx
interface TerminalPanelProps {
    cwd?: string;
    profile?: TerminalProfile;
}

export interface TerminalPanelHandle {
    fit: () => void;
}

const TerminalPanel = forwardRef<TerminalPanelHandle, TerminalPanelProps>(
    ({ cwd, profile }, ref) => {
        // Component implementation
    }
);
Use the ref handle to call fit() manually when the terminal panel is shown/hidden to ensure proper sizing.

Settings Component

Preferences panel for editor customization. Location: src/components/Settings/Settings.tsx
export interface EditorSettings {
    fontSize: number;
    fontFamily: string;
    lineHeight: number;
    tabSize: number;
    wordWrap: string;
    minimap: boolean;
    lineNumbers: string;
    cursorStyle: string;
    cursorBlinking: string;
    fontLigatures: boolean;
    scrollBeyondLastLine: boolean;
    renderWhitespace: string;
    bracketPairColorization: boolean;
}

Setting Types

// src/components/Settings/Settings.tsx:8-34
type SettingType = 'number' | 'select' | 'toggle' | 'font';

interface SettingDefinition<T = string | number | boolean> {
    key: string;
    displayName: string;
    monacoProperty: string;
    description?: string;
    type: SettingType;
    value: T;
    options?: SelectOption[];  // For 'select' type
    min?: number;              // For 'number' type
    max?: number;
    step?: number;
}

Top navigation bar with window controls and menus. Location: src/components/Navbar/Navbar.tsx
// src/components/Navbar/Navbar.tsx:10-21
interface NavbarProps {
    onOpenSettings: () => void;
}

export default function Navbar({ onOpenSettings }: NavbarProps) {
    return (
        <nav>
            <div className="navbar_left">
                <NavbarLogo />
                <NavbarMenu onOpenSettings={onOpenSettings} />
            </div>
            <NavbarOptions />
        </nav>
    );
}
Sub-components:
  • NavbarLogo: Application branding
  • NavbarMenu: File, Edit, View, Help menus
  • NavbarOptions: Window controls (minimize, maximize, close)

BottomBar Component

Status bar showing diagnostics and file information. Location: src/components/BottomBar/BottomBar.tsx
// src/components/BottomBar/BottomBar.tsx:8-43
interface BottomBarProps {
    activeFile?: TabData;
    cursorPos: { line: number; column: number };
    errors: number;
    warnings: number;
    onToggleProblems: () => void;
}

export default function BottomBar({ 
    activeFile, 
    cursorPos, 
    errors, 
    warnings, 
    onToggleProblems 
}: BottomBarProps) {
    return (
        <div className="bottom_bar">
            <ErrorsCount 
                errors={errors} 
                warnings={warnings} 
                onClick={onToggleProblems} 
            />
            
            <div className="rightside">
                <div className="item">
                    Ln {cursorPos.line}, Col {cursorPos.column}
                </div>
                {activeFile && (
                    <>
                        <div className="item">UTF-8</div>
                        <div className="item language">
                            <FontAwesomeIcon icon={typeIconMap[activeFile.type]?.icon} />
                            <span>{getLanguageDisplayName(activeFile.name)}</span>
                        </div>
                    </>
                )}
            </div>
        </div>
    )
}

Utility Components

EditorTabs

Tab bar for managing open files. Location: src/components/Editor/EditorTabs.tsx Features:
  • Close buttons with hover states
  • Active tab highlighting
  • Settings tab toggle
  • File type icons

ExplorerTree

Recursive tree component for file system. Location: src/components/Explorer/ExplorerTree.tsx Features:
  • Expand/collapse folders
  • File type icons with colors
  • Selection highlighting
  • Context menu integration

ImagePreview

Renders image files in the editor area. Location: src/components/Editor/ImagePreview.tsx
interface ImagePreviewProps {
    path: string;
    name: string;
}
Displays images using a custom blink-resource:// protocol handler.

ErrorsCount

Diagnostics counter in the status bar. Location: src/components/BottomBar/ErrorsCount.tsx Shows error and warning counts from Monaco’s validation.

Component Communication Patterns

Props Drilling

Blink uses props for parent-child communication:
// Parent passes handlers down
<Editor
    onContentChange={handleContentChange}
    onCursorChange={handleCursorChange}
/>

// Child calls handlers on events
function Editor({ onContentChange, onCursorChange }: EditorProps) {
    // ...
    onChange={(value) => onContentChange(value || '')}
    // ...
}

Ref Forwarding

TerminalPanel exposes imperative methods:
const terminalRef = useRef<TerminalPanelHandle>(null);

// Call fit() when panel is shown
useEffect(() => {
    if (showTerminal) {
        terminalRef.current?.fit();
    }
}, [showTerminal]);

<TerminalPanel ref={terminalRef} cwd={workingDir} />

IPC Bridge

Components access Electron APIs via window.electronAPI:
// Read file
const content = await window.electronAPI.invoke('file:read', filePath);

// Save file
await window.electronAPI.invoke('file:save', filePath, content);

// Listen for events
window.electronAPI.on('window-maximized', () => {
    setIsMaximized(true);
});

Styling System

Blink uses SCSS modules for component styling:
// src/components/Editor/_Editor.scss
.editor {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: $bg;
    
    &_main {
        flex: 1;
        overflow: hidden;
    }
}
Import pattern:
import './_Editor.scss';

Type Definitions

TabData

export interface TabData {
    name: string;
    path: string;
    content: string;
    type: FileType;
    isModified?: boolean;
}

TreeNode

export interface TreeNode {
    name: string;
    path: string;
    type: 'folder' | FileType;
    children?: TreeNode[];
    hasChildren?: boolean;
}

FileType

export type FileType = 
    | 'js' | 'jsx' | 'ts' | 'tsx' 
    | 'json' | 'html' | 'css' | 'scss'
    | 'md' | 'txt' | 'png' | 'jpg'
    | 'file' // Generic fallback

Next Steps

Architecture

Understand the overall system design

Building

Learn how to build and run Blink

Build docs developers (and LLMs) love