4.8 KiB
Architecture
Why this shape
The project should not become a giant main.go that mixes rendering, key handling, filesystem I/O and business rules. The architecture is split so the UI remains reactive and file operations stay isolated.
High-level modules
cmd/vcomEntry point, config loading, program startup.internal/configConfig schema, defaults, search paths and TOML parsing.internal/fsFilesystem model, directory scanning, previews and file operations.internal/themeTheme presets and style tokens.internal/uiBubble Tea model, key map, pane rendering, modal flow and layout.
State model
The Bubble Tea root model owns:
- terminal dimensions
- full config
- active browser pane
- left and right pane state
- center preview state
- transient modal state
- busy/status state for async work
This keeps the Elm-style update loop simple:
- key or resize event arrives
- model decides whether to mutate local state or start async work
- async work returns a typed message
- view renders from plain state
Pane model
Each side pane stores:
- current path
- scanned entries
- cursor index
- scroll offset
- cached directory sizes for entries calculated on demand
This is intentionally independent from rendering details, so the pane can be unit-tested later without Lip Gloss.
Preview pipeline
The center pane is driven only by the current selection from the active side pane.
Preview strategies:
- directory preview Shows child entries and summary data.
- text preview Reads up to a configured byte limit and displays text safely.
- image preview Detects dimensions and format through standard Go image decoders.
- binary fallback Avoids dumping junk bytes into the terminal.
Metadata shown above the preview:
- kind
- full path
- size
- created time
- modified time
- permissions
Directory size is expensive, so it is not calculated eagerly. Pressing Space starts an async scan and updates both:
- side-pane size column
- preview metadata widget
Configuration design
TOML is used because:
- it is readable in terminal workflows
- comments are first-class
- optional settings can stay commented out for manual enable/disable
The example config enables only MC-like columns by default:
namesizemodified
Additional columns are left commented out so users can literally uncomment them:
createdpermissionsextension
Other useful config areas:
- startup paths for left and right panes
- theme preset
- hidden file visibility
- sort field and reverse mode
- center pane width ratio
- confirmation behavior
- preview byte limits
- text wrapping in preview
- compact path display
Rendering strategy
The side panes are custom-rendered rather than built on top of the generic table bubble.
Reasoning:
- MC-like directory panes are not just tables
- we need tight control over selection styling, truncation and empty states
- the center pane uses a different rendering model than the side panes
Bubble usage:
bubbleteaevent loop and async commandsbubbles/keydeclarative key bindingsbubbles/textinputmkdir modalbubbles/viewportpreview scrolling surfacelipglossall layout and styling
Operations
The first operational slice is intentionally MC-core:
- copy selected entry to passive pane directory
- move selected entry to passive pane directory
- create directory in active pane
- delete selected entry
- refresh active pane
These operations are dispatched asynchronously to avoid freezing the TUI on large directories.
Overwrite handling is decided before the async operation begins:
- if the target does not exist, the operation runs directly
- if the target exists and overwrite confirmation is enabled, the UI opens a modal
- if overwrite confirmation is disabled, the operation replaces the target immediately
Theme system
Themes are token-based rather than style-object based.
Each preset defines semantic colors:
- background
- panel
- panel inactive
- border
- border active
- text
- muted text
- accent
- selection
- warning
- danger
- footer key
This allows future user-defined themes without touching UI logic.
The current UI also supports runtime theme cycling, but config remains the source of truth for default startup appearance.
Extension points
The architecture is designed to accept later additions without a rewrite:
- multi-select and batch operations
- file viewer/editor integration
- terminal image protocols
- tab history per pane
- bookmarks and quick-jump
- sort modes
- archive browsing
- search/filter overlay
- pluggable preview providers
Constraints worth keeping
- never calculate directory sizes during normal navigation
- never read full large files into memory for preview
- keep filesystem code out of
View() - keep styling decisions out of
internal/fs - prefer typed Tea messages over stringly-typed status events