New modules (13): - src/resize.zig: SIGWINCH terminal resize detection - src/drag.zig: Mouse drag state and Splitter panels - src/diagnostic.zig: Elm-style error messages with code snippets - src/debug.zig: Debug overlay (FPS, timing, widget count) - src/profile.zig: Performance profiling with scoped timers - src/sixel.zig: Sixel graphics encoding for terminal images - src/async_loop.zig: epoll-based async event loop with timers - src/compose.zig: Widget composition utilities - src/shortcuts.zig: Keyboard shortcut registry - src/widgets/logo.zig: ASCII art logo widget Enhanced modules: - src/layout.zig: Added Constraint.ratio(num, denom) - src/terminal.zig: Integrated resize handling - src/root.zig: Re-exports all new modules New examples (9): - resize_demo, splitter_demo, dirtree_demo - help_demo, markdown_demo, progress_demo - spinner_demo, syntax_demo, viewport_demo Package manager: - build.zig.zon: Zig package manager support Stats: 60+ source files, 186+ tests, 20 executables 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
134 lines
4.6 KiB
Zig
134 lines
4.6 KiB
Zig
//! Help Widget Demo - Shows keybinding help in different modes
|
|
//!
|
|
//! Run with: zig build help-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 Help = zcatui.widgets.Help;
|
|
const KeyBinding = zcatui.widgets.KeyBinding;
|
|
const HelpMode = zcatui.widgets.HelpMode;
|
|
|
|
const bindings = [_]KeyBinding{
|
|
.{ .key = "q", .description = "Quit", .group = "General" },
|
|
.{ .key = "?", .description = "Toggle help", .group = "General" },
|
|
.{ .key = "up/k", .description = "Move up", .group = "Navigation" },
|
|
.{ .key = "down/j", .description = "Move down", .group = "Navigation" },
|
|
.{ .key = "left/h", .description = "Move left", .group = "Navigation" },
|
|
.{ .key = "right/l", .description = "Move right", .group = "Navigation" },
|
|
.{ .key = "PgUp", .description = "Page up", .group = "Navigation" },
|
|
.{ .key = "PgDn", .description = "Page down", .group = "Navigation" },
|
|
.{ .key = "Home", .description = "Go to start", .group = "Navigation" },
|
|
.{ .key = "End", .description = "Go to end", .group = "Navigation" },
|
|
.{ .key = "Enter", .description = "Select item", .group = "Actions" },
|
|
.{ .key = "Space", .description = "Toggle selection", .group = "Actions" },
|
|
.{ .key = "Tab", .description = "Next panel", .group = "Actions" },
|
|
.{ .key = "Ctrl+C", .description = "Copy", .group = "Edit" },
|
|
.{ .key = "Ctrl+V", .description = "Paste", .group = "Edit" },
|
|
.{ .key = "Ctrl+Z", .description = "Undo", .group = "Edit" },
|
|
};
|
|
|
|
const modes = [_]HelpMode{ .single_line, .compact, .multi_line, .full };
|
|
const mode_names = [_][]const u8{ "Single Line", "Compact", "Multi Line", "Full" };
|
|
|
|
/// State for the demo
|
|
const State = struct {
|
|
current_mode: usize = 0,
|
|
running: bool = true,
|
|
};
|
|
|
|
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();
|
|
|
|
var state = State{};
|
|
|
|
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 >= '1' and c <= '4') {
|
|
state.current_mode = c - '1';
|
|
}
|
|
},
|
|
.left => {
|
|
if (state.current_mode > 0) state.current_mode -= 1;
|
|
},
|
|
.right => {
|
|
if (state.current_mode < modes.len - 1) state.current_mode += 1;
|
|
},
|
|
.esc => state.running = false,
|
|
else => {},
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn render(state: *State, area: Rect, buf: *Buffer) void {
|
|
// Main border
|
|
const block = Block.init()
|
|
.title(" Help Widget Demo ")
|
|
.setBorders(Borders.all)
|
|
.borderStyle((Style{}).fg(Color.cyan));
|
|
block.render(area, buf);
|
|
|
|
// Mode tabs
|
|
var x: u16 = 2;
|
|
for (mode_names, 0..) |name, i| {
|
|
const style = if (i == state.current_mode)
|
|
(Style{}).fg(Color.black).bg(Color.cyan).bold()
|
|
else
|
|
(Style{}).fg(Color.white);
|
|
|
|
_ = buf.setString(x, 1, " ", style);
|
|
_ = buf.setString(x + 1, 1, name, style);
|
|
_ = buf.setString(x + 1 + @as(u16, @intCast(name.len)), 1, " ", style);
|
|
x += @as(u16, @intCast(name.len)) + 3;
|
|
}
|
|
|
|
// Help content area
|
|
const content_area = Rect.init(1, 3, area.width -| 2, area.height -| 5);
|
|
|
|
const help_block = Block.init()
|
|
.setBorders(Borders.all)
|
|
.title(" Keybindings ")
|
|
.borderStyle((Style{}).fg(Color.rgb(80, 80, 80)));
|
|
help_block.render(content_area, buf);
|
|
|
|
const inner = Rect.init(2, 4, area.width -| 4, area.height -| 7);
|
|
|
|
// Help widget
|
|
const key_style = (Style{}).fg(Color.yellow).bold();
|
|
const help = Help.init(&bindings)
|
|
.setMode(modes[state.current_mode])
|
|
.setKeyStyle(key_style);
|
|
|
|
help.render(inner, buf);
|
|
|
|
// Footer
|
|
_ = buf.setString(
|
|
2,
|
|
area.height -| 1,
|
|
"Press 1-4 or left/right to change mode, q to quit",
|
|
(Style{}).fg(Color.rgb(100, 100, 100)),
|
|
);
|
|
}
|