sugarcraft/candy-files
Composer 安装命令:
composer create-project sugarcraft/candy-files
包简介
Dual-pane terminal file manager built on the SugarCraft stack. Inspired by superfile and Midnight Commander. Navigate two directories side-by-side, sort by name / mtime / size, multi-select, delete with confirmation, swap focus with Tab.
关键字:
README 文档
README
CandyFiles
Dual-pane terminal file manager built on the SugarCraft stack — port of yorukot/superfile, with the Midnight Commander look.
┌────────────────────────────────────┐ ┌────────────────────────────────────┐
│ /home/alice [name-asc] │ │ /var/log [mtime-desc] │
│ ────────────────── │ │ ────────────────── │
│ ▸ ../ DIR │ │ ../ DIR │
│ Documents/ DIR │ │ ▸✓ syslog 240KB │
│ Downloads/ DIR │ │ auth.log 12KB │
│ .config/ DIR │ │ Xorg.0.log 4.0KB │
│ notes.md 12KB │ │ │
│ todo.txt 512B │ │ │
└────────────────────────────────────┘ └────────────────────────────────────┘
Tab swap · ↑↓ jk move · Enter open · ← h up · space select · s sort · . hidden · d delete · r refresh · q quit · / search · t tabs · u undo
Run it
composer install ./bin/candyfiles [LEFT_DIR] [RIGHT_DIR]
Default: left pane = current directory, right pane = $HOME.
Keys
| Key | Action |
|---|---|
Tab |
Swap focus between panes |
↑ / k |
Move cursor up |
↓ / j |
Move cursor down |
Home / g |
Top of listing |
End / G |
Bottom of listing |
Enter / → |
Open directory (or no-op on a file) |
← / h |
Go up one directory |
Space |
Toggle selection on the entry under cursor + advance |
s |
Cycle sort order (name → mtime → size, asc / desc) |
. |
Toggle hidden-file visibility |
c |
Copy (selection or cursor) to inactive pane; y confirm |
m |
Move (selection or cursor) to inactive pane; y confirm |
R |
Rename (cursor entry); new name prompted; y confirm |
d |
Delete (selection or cursor); requires y confirm |
r |
Refresh active pane |
q |
Quit |
/ |
Start search mode; type to filter; Enter to open |
Escape |
Exit search mode |
t |
Duplicate current tab |
Ctrl+w |
Close current tab |
Ctrl+Tab |
Cycle to next tab |
Ctrl+Shift+Tab |
Cycle to previous tab |
u / Ctrl+z |
Undo last operation |
Architecture
The whole transition layer is pure — filesystem I/O is injected as a Closure(string $path): list<Entry> so every transition is unit-testable without tmp dirs or stat fixtures.
| File | Role |
|---|---|
Entry |
Value object: name, isDir, size, mtime, isLink, isHidden |
Sort |
Enum (NameAsc/NameDesc/MtimeAsc/MtimeDesc/SizeAsc/SizeDesc) + comparator |
Pane |
One pane: cwd, entries, cursor, selection set, sort, showHidden |
ConfirmState |
Pending-confirmation enum (None / DeleteSelected) |
Manager |
SugarCraft Model — orchestrates two panes, handles all keys + confirm gate |
FsLister |
Default lister: scandir + lstat against the live filesystem |
Renderer |
Pure view function — two pane boxes side-by-side + status line |
BulkRename |
Bulk rename engine: regex template + sequential {n}/{name}/{ext} placeholders |
PreviewPane |
File preview: ANSI image render via Mosaic for images, metadata block otherwise |
AsyncOps |
Async copy/move/rename via React\Promise — keeps TUI responsive during I/O |
Test plan
- 36 tests / 65 assertions
- Pure-state coverage:
Entry(size formatting, parent sentinel),Sort(every order × dirs-first × cycle),Pane(open / navigate / move / select / sort / hidden toggle / parent-path / join) Managerintegration: Tab swap, key dispatch per pane, confirm gate (darms,yconfirms, anything else cancels), refresh status
Demos
Navigation
Search
Multi-select and delete
Tab management
Sort cycling
Undo delete
Hidden-file toggle
Constructing Manager
Manager has 15 constructor parameters. Use the fluent builder for readability and to avoid parameter-order mistakes:
$manager = Manager::builder() ->withLeft($leftPane) ->withRight($rightPane) ->withActiveIdx(0) ->withStatus('') ->withConfirm(ConfirmState::None) ->withLister(fn(string $path): list<Entry> => FsLister::lister()($path)) ->withSearchQuery(null) ->withSearchResults([]) ->withSearchCursor(0) ->withTabs([]) ->withTabIndex(0) ->withShowTabBar(false) ->withUndoStack([]) ->withRedoStack([]) ->withPendingOpDest(null) ->withPendingOpType(null) ->build();
The direct constructor is kept for backward compatibility only — new code should use Manager::builder().
Status
Phase 10 entry — copy / move / rename / undo are wired. Three-phase confirm gate (c/m/R arms, y confirms, anything else cancels). Undo restores delete/move/rename; copy undo is informational (original preserved). Everything underneath (the pure-state transition layer) is already in place.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 2
- 点击次数: 7
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-28






