Skip to content

Repository files navigation

LCdrData

A native macOS dual-panel file manager in the orthodox tradition of Total Commander, ForkLift and Midnight Commander: two directory panels side by side, a function-key command bar along the bottom, and enough keyboard coverage that the mouse is optional.

"LCDR" stands for Lieutenant Commander — a nod to Lt. Cmdr. Data of Star Trek: The Next Generation.

Written in SwiftUI, sandboxed, and built with Bazel.

main window

Requirements

macOS 26.4 or later
Xcode 26.4 (at /Applications/Xcode.app, pinned in .bazelrc)
Bazel 9.2.0 — pinned in .bazelversion, so Bazelisk picks it up automatically
Tuist 4.182.0 — only needed for an Xcode project or UI tests

Nothing else to install. Bazel resolves its own dependencies on the first build.

Build and run

bazel run //LCdrData

That builds the app, unzips the bundle to a temporary directory and launches it. The terminal stays attached until you quit the app.

For a copy that outlives the run, build and unzip it yourself. Note that bazel build produces a zip, not a .app directory:

bazel build //LCdrData
unzip -o bazel-bin/LCdrData/LCdrData.zip -d /tmp/lcdr
open /tmp/lcdr/LCdrData.app

Both of the above are debug builds, whose entitlements include get-task-allow and the testmanagerd exceptions that app-hosted unit tests need in order to launch. For an optimised build carrying only the two production entitlement keys:

bazel build //LCdrData --config=release

Tests

bazel test //LCdrDataTests/...   # all unit tests
bazel test //LCdrDataTests/Core/Models   # one module's tests

Unit tests are written with Swift Testing, split across eight targets, one per module. bazel test //... adds compile coverage for the UI tests.

UI tests need a GUI login session and are driven by Tuist rather than Bazel, since Bazel's Apple test runner refuses macOS XCUITEST:

scripts/run-ui-tests.sh

Working in Xcode

Bazel cannot give you SwiftUI previews or a comfortable debugger, so the Xcode project is generated on demand with Tuist. It is not checked in:

tuist install    # resolve SPM dependencies
tuist generate   # generate and open the workspace

Both tools read the same dependency manifest, Tuist/Package.swift, so there is no second version list to keep in sync. See docs/BAZEL_MIGRATION.md for why the work is divided this way.

Features

Panels and navigation. Two resizable panels, each with a breadcrumb path bar that is also editable (⌘L, with ~ expansion), independent back/forward history, and a status bar. Tab moves between them.

Listing. Name, size, date and kind columns, sortable by header click, directories first within each sort group. Multi-select, type-ahead search, a togglable hidden-file filter, and a cursor model that keeps your place sensibly across reloads.

File operations. Copy and move between panels, trash, permanent delete, create folder and rename — with a progress overlay you can cancel and a conflict dialog offering overwrite, skip or auto-rename, each applicable to the whole batch.

Previewing. Quick Look via the system preview panel (Space or F3), open in the default application (F4), and reveal in Finder.

Multiple windows. ⌘N opens another window with its own panel state, seeded from the frontmost window; the file-system services are shared. Sessions are restored on relaunch.

Sandboxed by design. Access to folders outside the sandbox is granted through the standard open panel and remembered as security-scoped bookmarks, so a granted folder stays available across launches. Panels auto-refresh when their directory changes on disk.

Configuration. A KDL file with a built-in editor (⌘,) — see below.

Not implemented yet: tabs, archive browsing, an inline preview pane, a file-search service, and remappable keyboard shortcuts.

Keyboard shortcuts

Key Action
Tab Switch active panel
Return Enter directory, or rename a file
⌘↑ / Delete Go to parent directory
⌘↓ Open the focused item
⌘L Edit the path bar
⌘[ / ⌘] History back / forward
⌘R Refresh
⌘⇧. Toggle hidden files
⌘A / ⌘⇧A Select all / collapse selection to the focused row
⌘⌥C Copy selected paths to the clipboard
Space / F3 Quick Look
F2 Rename
F4 Open in default application
F5 / F6 Copy / move to the other panel
F7 New folder
F8 / ⌘⌫ Move to trash / delete permanently
Home / End First / last row
any letter Type-ahead search

Configuration

Settings live in a KDL 2.0 file, merged over the bundled defaults in LCdrData/Resources/DefaultConfig.kdl. ⌘, opens an editor showing the defaults beside your overrides; changes apply on Apply, without a restart. Because the app is sandboxed, the file itself lives inside the container:

~/Library/Containers/com.xvir.LCdrData/Data/Library/Application Support/com.xvir.LCdrData/config.kdl

It is created on the first Apply, and deleting it restores the defaults.

panel {
    show-hidden-files #false
    sort-by name
    sort-ascending #true
}

appearance {
    font-size 13
    date-format "yyyy-MM-dd HH:mm"
}

bookmarks {
    - "Projects|~/Projects"
    - "Downloads|~/Downloads"
}

bookmarks entries become the Favorites menu.

Architecture

The app is a stack of small Swift modules, each its own Bazel target, whose dependencies only ever point downward — layering enforced at analysis time by Bazel visibility rather than by review. docs/CURRENT_ARCH.md describes the modules, the services behind them, and how a keystroke becomes a file operation.

Documentation

docs/DESIGN.md Product spec — layout, behaviour, keyboard map, configuration
docs/CURRENT_ARCH.md The architecture as built
docs/CONTEXT.md Project vocabulary — panel, cursor, listing, session
docs/BAZEL_MIGRATION.md Why Bazel and Tuist both stay
AGENTS.md Contributor and agent guide: commands, conventions, gotchas

About

A native macOS dual-panel file manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages