//! Directory Tree Demo - File browser widget //! //! Run with: zig build dirtree-demo const std = @import("std"); const zcatui = @import("zcatui"); const Terminal = zcatui.Terminal; const Rect = zcatui.Rect; const Buffer = zcatui.Buffer; const Style = zcatui.Style; const Color = zcatui.Color; const Block = zcatui.widgets.Block; const Borders = zcatui.widgets.Borders; const DirectoryTree = zcatui.widgets.DirectoryTree; // TreeSymbols is in dirtree_mod, not exported directly const DirTreeSymbols = zcatui.widgets.dirtree_mod.TreeSymbols; /// State for the demo const State = struct { tree: *DirectoryTree, use_ascii: bool = false, running: bool = true, path: []const u8, }; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var term = try Terminal.init(allocator); defer term.deinit(); // Get current directory const start_path = std.fs.cwd().realpathAlloc(allocator, ".") catch "/tmp"; defer allocator.free(start_path); // Create directory tree var tree = DirectoryTree.init(allocator, start_path) catch |err| { std.debug.print("Failed to open directory: {}\n", .{err}); return; }; defer tree.deinit(); var state = State{ .tree = &tree, .path = start_path, }; while (state.running) { try term.drawWithContext(&state, render); if (try term.pollEvent(100)) |event| { switch (event) { .key => |key| { switch (key.code) { .char => |c| { if (c == 'q') state.running = false; if (c == 'j') state.tree.moveDown(); if (c == 'k') state.tree.moveUp(); if (c == 'l' or c == 'o') state.tree.toggleExpand() catch {}; if (c == 'h') state.tree.collapse() catch {}; if (c == '.') state.tree.toggleHidden() catch {}; if (c == 'i') state.use_ascii = !state.use_ascii; if (c == 'g') state.tree.goToTop(); if (c == 'G') state.tree.goToBottom(); }, .up => state.tree.moveUp(), .down => state.tree.moveDown(), .left => state.tree.collapse() catch {}, .right => state.tree.toggleExpand() catch {}, .enter => state.tree.toggleExpand() catch {}, .home => state.tree.goToTop(), .end => state.tree.goToBottom(), .esc => state.running = false, else => {}, } }, else => {}, } } } } fn render(state: *State, area: Rect, buf: *Buffer) void { // Main border const block = Block.init() .title(" Directory Tree ") .setBorders(Borders.all) .borderStyle((Style{}).fg(Color.cyan)); block.render(area, buf); // Path display var path_buf: [256]u8 = undefined; const current_path = state.tree.getSelectedPath() orelse state.path; const path_display = std.fmt.bufPrint(&path_buf, " {s} ", .{current_path}) catch "..."; _ = buf.setString(2, 1, path_display, (Style{}).fg(Color.yellow)); // Tree view const tree_area = Rect.init(1, 2, area.width -| 2, area.height -| 4); // Configure symbols based on preference if (state.use_ascii) { var configured = state.tree.setSymbols(DirTreeSymbols.ascii); configured.render(tree_area, buf); } else { state.tree.render(tree_area, buf); } // File info panel if (state.tree.getSelected()) |node| { var info_buf: [128]u8 = undefined; const info = std.fmt.bufPrint(&info_buf, "Selected: {s}", .{node.name}) catch "..."; _ = buf.setString(2, area.height -| 2, info, (Style{}).fg(Color.white)); } // Footer const footer = "j/k nav | l expand | h collapse | . hidden | i ascii | g/G top/bottom | q quit"; _ = buf.setString( 2, area.height -| 1, footer, (Style{}).fg(Color.rgb(100, 100, 100)), ); }