this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

at bef52fd916604b192bd718a16edb42e7630e1983 355 lines 17 kB view raw
1const std = @import("std"); 2const fmt = std.fmt; 3const heap = std.heap; 4const mem = std.mem; 5const meta = std.meta; 6 7const vaxis = @import("vaxis"); 8 9const log = std.log.scoped(.main); 10 11const ActiveSection = enum { 12 top, 13 mid, 14 btm, 15}; 16 17pub fn main() !void { 18 var gpa = heap.GeneralPurposeAllocator(.{}){}; 19 defer if (gpa.detectLeaks()) log.err("Memory leak detected!", .{}); 20 const alloc = gpa.allocator(); 21 22 // Users set up below the main function 23 const users_buf = try alloc.dupe(User, users[0..]); 24 const user_list = std.ArrayList(User).fromOwnedSlice(alloc, users_buf); 25 defer user_list.deinit(); 26 var user_mal = std.MultiArrayList(User){}; 27 for (users_buf[0..]) |user| try user_mal.append(alloc, user); 28 defer user_mal.deinit(alloc); 29 30 var tty = try vaxis.Tty.init(); 31 defer tty.deinit(); 32 var tty_buf_writer = tty.bufferedWriter(); 33 defer tty_buf_writer.flush() catch {}; 34 const tty_writer = tty_buf_writer.writer().any(); 35 var vx = try vaxis.init(alloc, .{ 36 .kitty_keyboard_flags = .{ .report_events = true }, 37 }); 38 defer vx.deinit(alloc, tty.anyWriter()); 39 40 var loop: vaxis.Loop(union(enum) { 41 key_press: vaxis.Key, 42 winsize: vaxis.Winsize, 43 table_upd, 44 }) = .{ .tty = &tty, .vaxis = &vx }; 45 try loop.init(); 46 try loop.start(); 47 defer loop.stop(); 48 try vx.enterAltScreen(tty.anyWriter()); 49 try vx.queryTerminal(tty.anyWriter(), 250 * std.time.ns_per_ms); 50 51 const logo = 52 \\░█░█░█▀█░█░█░▀█▀░█▀▀░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░ 53 \\░▀▄▀░█▀█░▄▀▄░░█░░▀▀█░░░░█░░█▀█░█▀▄░█░░░█▀▀░ 54 \\░░▀░░▀░▀░▀░▀░▀▀▀░▀▀▀░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░ 55 ; 56 const title_logo = vaxis.Cell.Segment{ 57 .text = logo, 58 .style = .{}, 59 }; 60 const title_info = vaxis.Cell.Segment{ 61 .text = "===A Demo of the the Vaxis Table Widget!===", 62 .style = .{}, 63 }; 64 const title_disclaimer = vaxis.Cell.Segment{ 65 .text = "(All data is non-sensical & LLM generated.)", 66 .style = .{}, 67 }; 68 var title_segs = [_]vaxis.Cell.Segment{ title_logo, title_info, title_disclaimer }; 69 70 var cmd_input = vaxis.widgets.TextInput.init(alloc, &vx.unicode); 71 defer cmd_input.deinit(); 72 73 // Colors 74 const active_bg: vaxis.Cell.Color = .{ .rgb = .{ 64, 128, 255 } }; 75 const selected_bg: vaxis.Cell.Color = .{ .rgb = .{ 32, 64, 255 } }; 76 const other_bg: vaxis.Cell.Color = .{ .rgb = .{ 32, 32, 48 } }; 77 78 // Table Context 79 var demo_tbl: vaxis.widgets.Table.TableContext = .{ 80 .active_bg = active_bg, 81 .active_fg = .{ .rgb = .{ 0, 0, 0 } }, 82 .row_bg_1 = .{ .rgb = .{ 8, 8, 8 } }, 83 .selected_bg = selected_bg, 84 .header_names = .{ .custom = &.{ "First", "Last", "Username", "Phone#", "Email" } }, 85 //.header_align = .left, 86 .col_indexes = .{ .by_idx = &.{ 0, 1, 2, 4, 3 } }, 87 //.col_align = .{ .by_idx = &.{ .left, .left, .center, .center, .left } }, 88 //.col_align = .{ .all = .center }, 89 //.header_borders = true, 90 //.col_borders = true, 91 //.col_width = .{ .static_all = 15 }, 92 //.col_width = .{ .dynamic_header_len = 3 }, 93 //.col_width = .{ .static_individual = &.{ 10, 20, 15, 25, 15 } }, 94 //.col_width = .dynamic_fill, 95 //.y_off = 10, 96 }; 97 defer if (demo_tbl.sel_rows) |rows| alloc.free(rows); 98 99 // TUI State 100 var active: ActiveSection = .mid; 101 var moving = false; 102 var see_content = false; 103 104 // Create an Arena Allocator for easy allocations on each Event. 105 var event_arena = heap.ArenaAllocator.init(alloc); 106 defer event_arena.deinit(); 107 while (true) { 108 defer _ = event_arena.reset(.retain_capacity); 109 defer tty_buf_writer.flush() catch {}; 110 const event_alloc = event_arena.allocator(); 111 const event = loop.nextEvent(); 112 113 switch (event) { 114 .key_press => |key| keyEvt: { 115 // Close the Program 116 if (key.matches('c', .{ .ctrl = true })) { 117 break; 118 } 119 // Refresh the Screen 120 if (key.matches('l', .{ .ctrl = true })) { 121 vx.queueRefresh(); 122 break :keyEvt; 123 } 124 // Enter Moving State 125 if (key.matches('w', .{ .ctrl = true })) { 126 moving = !moving; 127 break :keyEvt; 128 } 129 // Command State 130 if (active != .btm and 131 key.matchesAny(&.{ ':', '/', 'g', 'G' }, .{})) 132 { 133 active = .btm; 134 cmd_input.clearAndFree(); 135 try cmd_input.update(.{ .key_press = key }); 136 break :keyEvt; 137 } 138 139 switch (active) { 140 .top => { 141 if (key.matchesAny(&.{ vaxis.Key.down, 'j' }, .{}) and moving) active = .mid; 142 }, 143 .mid => midEvt: { 144 if (moving) { 145 if (key.matchesAny(&.{ vaxis.Key.up, 'k' }, .{})) active = .top; 146 if (key.matchesAny(&.{ vaxis.Key.down, 'j' }, .{})) active = .btm; 147 break :midEvt; 148 } 149 // Change Row 150 if (key.matchesAny(&.{ vaxis.Key.up, 'k' }, .{})) demo_tbl.row -|= 1; 151 if (key.matchesAny(&.{ vaxis.Key.down, 'j' }, .{})) demo_tbl.row +|= 1; 152 // Change Column 153 if (key.matchesAny(&.{ vaxis.Key.left, 'h' }, .{})) demo_tbl.col -|= 1; 154 if (key.matchesAny(&.{ vaxis.Key.right, 'l' }, .{})) demo_tbl.col +|= 1; 155 // Select/Unselect Row 156 if (key.matches(vaxis.Key.space, .{})) { 157 const rows = demo_tbl.sel_rows orelse createRows: { 158 demo_tbl.sel_rows = try alloc.alloc(u16, 1); 159 break :createRows demo_tbl.sel_rows.?; 160 }; 161 var rows_list = std.ArrayList(u16).fromOwnedSlice(alloc, rows); 162 for (rows_list.items, 0..) |row, idx| { 163 if (row != demo_tbl.row) continue; 164 _ = rows_list.orderedRemove(idx); 165 break; 166 } else try rows_list.append(demo_tbl.row); 167 demo_tbl.sel_rows = try rows_list.toOwnedSlice(); 168 } 169 // See Row Content 170 if (key.matches(vaxis.Key.enter, .{}) or key.matches('j', .{ .ctrl = true })) see_content = !see_content; 171 }, 172 .btm => { 173 if (key.matchesAny(&.{ vaxis.Key.up, 'k' }, .{}) and moving) active = .mid 174 // Run Command and Clear Command Bar 175 else if (key.matchExact(vaxis.Key.enter, .{}) or key.matchExact('j', .{ .ctrl = true })) { 176 const cmd = try cmd_input.toOwnedSlice(); 177 defer alloc.free(cmd); 178 if (mem.eql(u8, ":q", cmd) or 179 mem.eql(u8, ":quit", cmd) or 180 mem.eql(u8, ":exit", cmd)) return; 181 if (mem.eql(u8, "G", cmd)) { 182 demo_tbl.row = @intCast(user_list.items.len - 1); 183 active = .mid; 184 } 185 if (cmd.len >= 2 and mem.eql(u8, "gg", cmd[0..2])) { 186 const goto_row = fmt.parseInt(u16, cmd[2..], 0) catch 0; 187 demo_tbl.row = goto_row; 188 active = .mid; 189 } 190 } else try cmd_input.update(.{ .key_press = key }); 191 }, 192 } 193 moving = false; 194 }, 195 .winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws), 196 else => {}, 197 } 198 199 // Content 200 seeRow: { 201 if (!see_content) { 202 demo_tbl.active_content_fn = null; 203 demo_tbl.active_ctx = &{}; 204 break :seeRow; 205 } 206 const RowContext = struct { 207 row: []const u8, 208 bg: vaxis.Color, 209 }; 210 const row_ctx = RowContext{ 211 .row = try fmt.allocPrint(event_alloc, "Row #: {d}", .{demo_tbl.row}), 212 .bg = demo_tbl.active_bg, 213 }; 214 demo_tbl.active_ctx = &row_ctx; 215 demo_tbl.active_content_fn = struct { 216 fn see(win: *vaxis.Window, ctx_raw: *const anyopaque) !u16 { 217 const ctx: *const RowContext = @alignCast(@ptrCast(ctx_raw)); 218 win.height = 5; 219 const see_win = win.child(.{ 220 .x_off = 0, 221 .y_off = 1, 222 .width = win.width, 223 .height = 4, 224 }); 225 see_win.fill(.{ .style = .{ .bg = ctx.bg } }); 226 const content_logo = 227 \\ 228 \\░█▀▄░█▀█░█░█░░░█▀▀░█▀█░█▀█░▀█▀░█▀▀░█▀█░▀█▀ 229 \\░█▀▄░█░█░█▄█░░░█░░░█░█░█░█░░█░░█▀▀░█░█░░█░ 230 \\░▀░▀░▀▀▀░▀░▀░░░▀▀▀░▀▀▀░▀░▀░░▀░░▀▀▀░▀░▀░░▀░ 231 ; 232 const content_segs: []const vaxis.Cell.Segment = &.{ 233 .{ 234 .text = ctx.row, 235 .style = .{ .bg = ctx.bg }, 236 }, 237 .{ 238 .text = content_logo, 239 .style = .{ .bg = ctx.bg }, 240 }, 241 }; 242 _ = see_win.print(content_segs, .{}); 243 return see_win.height; 244 } 245 }.see; 246 loop.postEvent(.table_upd); 247 } 248 249 // Sections 250 // - Window 251 const win = vx.window(); 252 win.clear(); 253 254 // - Top 255 const top_div = 6; 256 const top_bar = win.child(.{ 257 .x_off = 0, 258 .y_off = 0, 259 .width = win.width, 260 .height = win.height / top_div, 261 }); 262 for (title_segs[0..]) |*title_seg| 263 title_seg.style.bg = if (active == .top) selected_bg else other_bg; 264 top_bar.fill(.{ .style = .{ 265 .bg = if (active == .top) selected_bg else other_bg, 266 } }); 267 const logo_bar = vaxis.widgets.alignment.center( 268 top_bar, 269 44, 270 top_bar.height - (top_bar.height / 3), 271 ); 272 _ = logo_bar.print(title_segs[0..], .{ .wrap = .word }); 273 274 // - Middle 275 const middle_bar = win.child(.{ 276 .x_off = 0, 277 .y_off = win.height / top_div, 278 .width = win.width, 279 .height = win.height - (top_bar.height + 1), 280 }); 281 if (user_list.items.len > 0) { 282 demo_tbl.active = active == .mid; 283 try vaxis.widgets.Table.drawTable( 284 event_alloc, 285 middle_bar, 286 //users_buf[0..], 287 //user_list, 288 user_mal, 289 &demo_tbl, 290 ); 291 } 292 293 // - Bottom 294 const bottom_bar = win.child(.{ 295 .x_off = 0, 296 .y_off = win.height - 1, 297 .width = win.width, 298 .height = 1, 299 }); 300 if (active == .btm) bottom_bar.fill(.{ .style = .{ .bg = active_bg } }); 301 cmd_input.draw(bottom_bar); 302 303 // Render the screen 304 try vx.render(tty_writer); 305 } 306} 307 308/// User Struct 309pub const User = struct { 310 first: []const u8, 311 last: []const u8, 312 user: []const u8, 313 email: ?[]const u8 = null, 314 phone: ?[]const u8 = null, 315}; 316 317// Users Array 318const users = [_]User{ 319 .{ .first = "Nancy", .last = "Dudley", .user = "angela73", .email = "brian47@rodriguez.biz", .phone = null }, 320 .{ .first = "Emily", .last = "Thornton", .user = "mrogers", .email = null, .phone = "(558)888-8604x094" }, 321 .{ .first = "Kyle", .last = "Huff", .user = "xsmith", .email = null, .phone = "301.127.0801x12398" }, 322 .{ .first = "Christine", .last = "Dodson", .user = "amandabradley", .email = "cheryl21@sullivan.com", .phone = null }, 323 .{ .first = "Nathaniel", .last = "Kennedy", .user = "nrobinson", .email = null, .phone = null }, 324 .{ .first = "Laura", .last = "Leon", .user = "dawnjones", .email = "fjenkins@patel.com", .phone = "1833013180" }, 325 .{ .first = "Patrick", .last = "Landry", .user = "michaelhutchinson", .email = "daniel17@medina-wallace.net", .phone = "+1-634-486-6444x964" }, 326 .{ .first = "Tammy", .last = "Hall", .user = "jamessmith", .email = null, .phone = "(926)810-3385x22059" }, 327 .{ .first = "Stephanie", .last = "Anderson", .user = "wgillespie", .email = "campbelljaime@yahoo.com", .phone = null }, 328 .{ .first = "Jennifer", .last = "Williams", .user = "shawn60", .email = null, .phone = "611-385-4771x97523" }, 329 .{ .first = "Elizabeth", .last = "Ortiz", .user = "jennifer76", .email = "johnbradley@delgado.info", .phone = null }, 330 .{ .first = "Stacy", .last = "Mays", .user = "scottgonzalez", .email = "kramermatthew@gmail.com", .phone = null }, 331 .{ .first = "Jennifer", .last = "Smith", .user = "joseph75", .email = "masseyalexander@hill-moore.net", .phone = null }, 332 .{ .first = "Gary", .last = "Hammond", .user = "brittany26", .email = null, .phone = null }, 333 .{ .first = "Lisa", .last = "Johnson", .user = "tina28", .email = null, .phone = "850-606-2978x1081" }, 334 .{ .first = "Zachary", .last = "Hopkins", .user = "vargasmichael", .email = null, .phone = null }, 335 .{ .first = "Joshua", .last = "Kidd", .user = "ghanna", .email = "jbrown@yahoo.com", .phone = null }, 336 .{ .first = "Dawn", .last = "Jones", .user = "alisonlindsey", .email = null, .phone = null }, 337 .{ .first = "Monica", .last = "Berry", .user = "barbara40", .email = "michael00@hotmail.com", .phone = "(295)346-6453x343" }, 338 .{ .first = "Shannon", .last = "Roberts", .user = "krystal37", .email = null, .phone = "980-920-9386x454" }, 339 .{ .first = "Thomas", .last = "Mitchell", .user = "williamscorey", .email = "richardduncan@roberts.com", .phone = null }, 340 .{ .first = "Nicole", .last = "Shaffer", .user = "rogerstroy", .email = null, .phone = "(570)128-5662" }, 341 .{ .first = "Edward", .last = "Bennett", .user = "andersonchristina", .email = null, .phone = null }, 342 .{ .first = "Duane", .last = "Howard", .user = "pcarpenter", .email = "griffithwayne@parker.net", .phone = null }, 343 .{ .first = "Mary", .last = "Brown", .user = "kimberlyfrost", .email = "perezsara@anderson-andrews.net", .phone = null }, 344 .{ .first = "Pamela", .last = "Sloan", .user = "kvelez", .email = "huynhlacey@moore-bell.biz", .phone = "001-359-125-1393x8716" }, 345 .{ .first = "Timothy", .last = "Charles", .user = "anthony04", .email = "morrissara@hawkins.info", .phone = "+1-619-369-9572" }, 346 .{ .first = "Sydney", .last = "Torres", .user = "scott42", .email = "asnyder@mitchell.net", .phone = null }, 347 .{ .first = "John", .last = "Jones", .user = "anthonymoore", .email = null, .phone = "701.236.0571x99622" }, 348 .{ .first = "Erik", .last = "Johnson", .user = "allisonsanders", .email = null, .phone = null }, 349 .{ .first = "Donna", .last = "Kirk", .user = "laurie81", .email = null, .phone = null }, 350 .{ .first = "Karina", .last = "White", .user = "uperez", .email = null, .phone = null }, 351 .{ .first = "Jesse", .last = "Schwartz", .user = "ryan60", .email = "latoyawilliams@gmail.com", .phone = null }, 352 .{ .first = "Cindy", .last = "Romero", .user = "christopher78", .email = "faulknerchristina@gmail.com", .phone = "780.288.2319x583" }, 353 .{ .first = "Tyler", .last = "Sanders", .user = "bennettjessica", .email = null, .phone = "1966269423" }, 354 .{ .first = "Pamela", .last = "Carter", .user = "zsnyder", .email = null, .phone = "125-062-9130x58413" }, 355};