地圖 (Jido) is a lightweight Unix TUI file explorer designed for speed and simplicity.

feat: Added force delete keybind. It's unbound by default. docs: Updated README and example-config.json to show how to unbound keybinds. chore: bumped patch version and updated CHANGELOG.

+4
CHANGELOG.md
··· 1 # Changelog 2 3 ## v0.9.5 (2025-03-29) 4 - feat: Added [-v | --version] and [-h | --help] args. 5
··· 1 # Changelog 2 3 + ## v0.9.6 (2025-03-31) 4 + - feat: Added ability to unbound keybinds. 5 + - feat: Added force delete keybind. It's unbound by default. 6 + 7 ## v0.9.5 (2025-03-29) 8 - feat: Added [-v | --version] and [-h | --help] args. 9
+1 -1
PROJECT_BOARD.md
··· 12 - [ ] Copy files. 13 - [ ] Copy folders. 14 - [ ] Keybind to unzip archives. 15 - - [ ] Keybind to hard delete items (bypass trash). 16 - [x] Ability to unbind keys. 17 18 ### Refactors
··· 12 - [ ] Copy files. 13 - [ ] Copy folders. 14 - [ ] Keybind to unzip archives. 15 + - [x] Keybind to hard delete items (bypass trash). 16 - [x] Ability to unbind keys. 17 18 ### Refactors
+3 -1
README.md
··· 111 .enter_command_mode: ?Char = ':', 112 .jump_top: ?Char = 'g', 113 .jump_bottom: ?Char = 'G', 114 - .toggle_verbose_file_information: ?Char = 'v' 115 } 116 117 NotificationStyles = struct {
··· 111 .enter_command_mode: ?Char = ':', 112 .jump_top: ?Char = 'g', 113 .jump_bottom: ?Char = 'G', 114 + .toggle_verbose_file_information: ?Char = 'v', 115 + .force_delete: ?Char = null -- Files deleted this way are 116 + not recoverable 117 } 118 119 NotificationStyles = struct {
+1 -1
build.zig
··· 2 const builtin = @import("builtin"); 3 4 ///Must match the `version` in `build.zig.zon`. 5 - const version = std.SemanticVersion{ .major = 0, .minor = 9, .patch = 5 }; 6 7 const targets: []const std.Target.Query = &.{ 8 .{ .cpu_arch = .aarch64, .os_tag = .macos },
··· 2 const builtin = @import("builtin"); 3 4 ///Must match the `version` in `build.zig.zon`. 5 + const version = std.SemanticVersion{ .major = 0, .minor = 9, .patch = 6 }; 6 7 const targets: []const std.Target.Query = &.{ 8 .{ .cpu_arch = .aarch64, .os_tag = .macos },
+1 -1
build.zig.zon
··· 1 .{ 2 .name = .jido, 3 .fingerprint = 0xee45eabe36cafb57, 4 - .version = "0.9.5", 5 .minimum_zig_version = "0.14.0", 6 7 .dependencies = .{
··· 1 .{ 2 .name = .jido, 3 .fingerprint = 0xee45eabe36cafb57, 4 + .version = "0.9.6", 5 .minimum_zig_version = "0.14.0", 6 7 .dependencies = .{
+2 -1
example-config.json
··· 4 "show_images": true, 5 "preview_file": true, 6 "keybinds": { 7 - "toggle_hidden_files": "h" 8 }, 9 "styles": { 10 "selected_list_item": {
··· 4 "show_images": true, 5 "preview_file": true, 6 "keybinds": { 7 + "toggle_hidden_files": "h", 8 + "force_delete": null 9 }, 10 "styles": { 11 "selected_list_item": {
+1
src/config.zig
··· 209 jump_top: ?Char = @enumFromInt('g'), 210 jump_bottom: ?Char = @enumFromInt('G'), 211 toggle_verbose_file_information: ?Char = @enumFromInt('v'), 212 }; 213 214 const Styles = struct {
··· 209 jump_top: ?Char = @enumFromInt('g'), 210 jump_bottom: ?Char = @enumFromInt('G'), 211 toggle_verbose_file_information: ?Char = @enumFromInt('v'), 212 + force_delete: ?Char = null, 213 }; 214 215 const Styles = struct {
+16 -3
src/event_handlers.zig
··· 174 app.text_input.insertSliceAtCursor(":") catch {}; 175 app.state = .command; 176 }, 177 - .jump_bottom => { 178 - app.directories.entries.selectLast(); 179 - }, 180 .jump_top => app.directories.entries.selectFirst(), 181 .toggle_verbose_file_information => app.drawer.verbose = !app.drawer.verbose, 182 } 183 } else { 184 switch (key.codepoint) {
··· 174 app.text_input.insertSliceAtCursor(":") catch {}; 175 app.state = .command; 176 }, 177 + .jump_bottom => app.directories.entries.selectLast(), 178 .jump_top => app.directories.entries.selectFirst(), 179 .toggle_verbose_file_information => app.drawer.verbose = !app.drawer.verbose, 180 + .force_delete => { 181 + const entry = lbl: { 182 + const entry = app.directories.getSelected() catch { 183 + try app.notification.writeErr(.UnableToDelete); 184 + return; 185 + }; 186 + if (entry) |e| break :lbl e else return; 187 + }; 188 + 189 + app.directories.dir.deleteTree(entry.name) catch { 190 + try app.notification.writeErr(.UnableToDelete); 191 + return; 192 + }; 193 + app.directories.removeSelected(); 194 + }, 195 } 196 } else { 197 switch (key.codepoint) {