//! Clipboard demo for zcatui. //! //! Demonstrates OSC 52 clipboard support: //! - Copy text to system clipboard //! - Copy to primary selection (X11) //! - Clear clipboard //! //! Run with: zig build clipboard-demo //! //! Note: OSC 52 support varies by terminal. If clipboard operations //! don't work, check your terminal's documentation. const std = @import("std"); const zcatui = @import("zcatui"); const Terminal = zcatui.Terminal; const Buffer = zcatui.Buffer; const Rect = zcatui.Rect; const Style = zcatui.Style; const Color = zcatui.Color; const Event = zcatui.Event; const KeyCode = zcatui.KeyCode; const Layout = zcatui.Layout; const Constraint = zcatui.Constraint; const Block = zcatui.widgets.Block; const Borders = zcatui.widgets.Borders; const Paragraph = zcatui.widgets.Paragraph; const Clipboard = zcatui.clipboard; const AppState = struct { running: bool = true, last_action: []const u8 = "Press a key to test clipboard", copy_count: u32 = 0, }; 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 = AppState{}; while (state.running) { try term.drawWithContext(&state, render); if (try term.pollEvent(100)) |event| { try handleEvent(&state, &term, allocator, event); } } } fn handleEvent(state: *AppState, term: *Terminal, allocator: std.mem.Allocator, event: Event) !void { const writer = term.backend.stdout.deprecatedWriter(); switch (event) { .key => |key| { switch (key.code) { .esc => state.running = false, .char => |c| { switch (c) { 'q', 'Q' => state.running = false, '1' => { state.copy_count += 1; var buf: [64]u8 = undefined; const text = std.fmt.bufPrint(&buf, "Hello from zcatui #{d}!", .{state.copy_count}) catch "Hello!"; try Clipboard.copy(allocator, writer, text); state.last_action = "Copied text to clipboard (Ctrl+V to paste)"; }, '2' => { try Clipboard.copySmallTo(writer, "Primary selection text", .primary); state.last_action = "Copied to primary (middle-click to paste)"; }, '3' => { try Clipboard.copySmallTo(writer, "Both clipboard and primary!", .both); state.last_action = "Copied to both clipboard and primary"; }, 'c', 'C' => { try Clipboard.clear(writer); state.last_action = "Cleared clipboard"; }, else => {}, } }, else => {}, } }, else => {}, } } fn render(state: *AppState, area: Rect, buf: *Buffer) void { // Main layout const chunks = Layout.vertical(&.{ Constraint.length(3), // Title Constraint.length(8), // Actions Constraint.length(5), // Status Constraint.min(0), // Help }).split(area); // Title renderTitle(chunks.get(0), buf); // Actions renderActions(chunks.get(1), buf); // Status renderStatus(state, chunks.get(2), buf); // Help renderHelp(chunks.get(3), buf); } fn renderTitle(area: Rect, buf: *Buffer) void { const block = Block.init() .title(" Clipboard Demo (OSC 52) ") .setBorders(Borders.all) .style(Style.default.fg(Color.cyan)); block.render(area, buf); const inner = block.inner(area); _ = buf.setString(inner.left(), inner.top(), "Test clipboard operations in your terminal", Style.default); } fn renderActions(area: Rect, buf: *Buffer) void { const block = Block.init() .title(" Available Actions ") .setBorders(Borders.all) .style(Style.default.fg(Color.yellow)); block.render(area, buf); const inner = block.inner(area); var y = inner.top(); const actions = [_][]const u8{ "1 - Copy text to clipboard", "2 - Copy to primary selection (X11)", "3 - Copy to both clipboard and primary", "c - Clear clipboard", "q - Quit", }; for (actions) |action| { if (y < inner.bottom()) { _ = buf.setString(inner.left(), y, action, Style.default); y += 1; } } } fn renderStatus(state: *AppState, area: Rect, buf: *Buffer) void { const block = Block.init() .title(" Last Action ") .setBorders(Borders.all) .style(Style.default.fg(Color.green)); block.render(area, buf); const inner = block.inner(area); _ = buf.setString(inner.left(), inner.top(), state.last_action, Style.default.fg(Color.white).bold()); if (state.copy_count > 0) { var count_buf: [32]u8 = undefined; const count_text = std.fmt.bufPrint(&count_buf, "Total copies: {d}", .{state.copy_count}) catch "?"; _ = buf.setString(inner.left(), inner.top() + 1, count_text, Style.default.dim()); } } fn renderHelp(area: Rect, buf: *Buffer) void { const block = Block.init() .title(" Notes ") .setBorders(Borders.all) .style(Style.default.fg(Color.blue)); block.render(area, buf); const inner = block.inner(area); var y = inner.top(); const notes = [_][]const u8{ "OSC 52 clipboard support varies by terminal.", "Supported: xterm, iTerm2, kitty, alacritty, WezTerm, foot", "tmux: enable with 'set -g set-clipboard on'", "Some terminals disable OSC 52 by default for security.", }; for (notes) |note| { if (y < inner.bottom()) { _ = buf.setString(inner.left(), y, note, Style.default.dim()); y += 1; } } }