╭ changed ──────╮ │src/ui/Home.zig│ ╰───────────────╯ ╭─────────────────────────────────────────────────────────────────────╮ │27 haxy_moment: rp.Repo(.xit, repo_opts).DB.HashMap(.read_only),│ │28 ) !Self { │ │29 return .{ │ │30 - .header = Header.init(), │ │30 + .header = try Header.init(arena), │ │31 .users = try Users.init(repo_opts, arena, haxy_moment), │ │32 .repos = try Repos.init(repo_opts, arena, haxy_moment), │ │33 .auth = Auth.init(), │ ╰─────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Home/Header.zig│ ╰──────────────────────╯ ╭────────────────────────────────────────────────────────╮ │15 │ │16 const Self = @This(); │ │17 │ │18 -pub fn init() Self { │ │18 +pub fn init(arena: *std.heap.ArenaAllocator) !Self {│ │19 return .{ │ │20 - .title = ui.Title.init("haxy"), │ │20 + .title = try ui.Title.init(arena, "haxy"), │ │21 .auth_tab = AuthTab.init(), │ │22 }; │ │23 } │ ╰────────────────────────────────────────────────────────╯ ╭ changed ───────╮ │src/ui/Title.zig│ ╰────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │13 │ │14 const Self = @This(); │ │15 │ │16 -pub fn init(comptime orig_content: []const u8) Self { │ │17 - const rendered = comptime renderTitle(orig_content); │ │16 +pub fn init(arena: *std.heap.ArenaAllocator, orig_content: []const u8) !Self {│ │17 + const rendered = try renderTitle(arena.allocator(), orig_content); │ │18 + // count cells in the first row: one per UTF-8 codepoint start │ │19 + var width: usize = 0; │ │20 + for (rendered) |byte| { │ │21 + if (byte == '\n') break; │ │22 + if (byte & 0xC0 != 0x80) width += 1; │ │23 + } │ │24 return .{ │ │25 .content = rendered, │ │20 - .width = comptime blk: { │ │21 - // count cells in the first row: one per UTF-8 codepoint start │ │22 - var width: usize = 0; │ │23 - for (rendered) |byte| { │ │24 - if (byte == '\n') break; │ │25 - if (byte & 0xC0 != 0x80) width += 1; │ │26 - } │ │27 - break :blk width; │ │28 - }, │ │26 + .width = width, │ │27 }; │ │28 } │ │29 │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │525 }; │ │526 } │ │527 │ │530 -fn renderTitle(comptime input: []const u8) []const u8 { │ │531 - comptime { │ │532 - @setEvalBranchQuota(100_000); │ │533 - var rows: [3][]const u8 = .{ "", "", "" }; │ │534 - var first = true; │ │535 - for (input) |c| { │ │536 - const glyph = glyphFor(c) orelse continue; │ │528 +fn renderTitle(allocator: std.mem.Allocator, input: []const u8) ![]const u8 { │ │529 + var rows: [3]std.ArrayList(u8) = .{ .empty, .empty, .empty }; │ │530 + defer for (&rows) |*r| r.deinit(allocator); │ │531 │ │538 - // row-aligned dither: keep on-pixels only at even bitmap rows. │ │539 - var kept: [9][6]bool = std.mem.zeroes([9][6]bool); │ │540 - for (0..9) |r| { │ │541 - if (r % 2 != 0) continue; │ │542 - for (0..6) |col| { │ │543 - kept[r][col] = glyph[r][col] == '#'; │ │544 - } │ │532 + var first = true; │ │533 + for (input) |c| { │ │534 + const glyph = glyphFor(c) orelse continue; │ │535 + │ │536 + // row-aligned dither: keep on-pixels only at even bitmap rows. │ │537 + var kept: [9][6]bool = std.mem.zeroes([9][6]bool); │ │538 + for (0..9) |r| { │ │539 + if (r % 2 != 0) continue; │ │540 + for (0..6) |col| { │ │541 + kept[r][col] = glyph[r][col] == '#'; │ │542 } │ │543 + } │ │544 │ │547 - if (!first) { │ │548 - for (&rows) |*r| { │ │549 - r.* = r.* ++ " "; │ │550 - } │ │551 - } │ │552 - first = false; │ │545 + if (!first) { │ │546 + for (&rows) |*r| try r.append(allocator, ' '); │ │547 + } │ │548 + first = false; │ │549 │ │554 - for (0..3) |text_row| { │ │555 - for (0..3) |text_col| { │ │556 - var pattern: u6 = 0; │ │557 - for (0..3) |sub_row| { │ │558 - for (0..2) |sub_col| { │ │559 - const row_idx = text_row * 3 + sub_row; │ │560 - const col_idx = text_col * 2 + sub_col; │ │561 - if (kept[row_idx][col_idx]) { │ │562 - pattern |= @as(u6, 1) << @intCast(sub_row * 2 + sub_col);│ │563 - } │ │550 + for (0..3) |text_row| { │ │551 + for (0..3) |text_col| { │ │552 + var pattern: u6 = 0; │ │553 + for (0..3) |sub_row| { │ │554 + for (0..2) |sub_col| { │ │555 + const row_idx = text_row * 3 + sub_row; │ │556 + const col_idx = text_col * 2 + sub_col; │ │557 + if (kept[row_idx][col_idx]) { │ │558 + pattern |= @as(u6, 1) << @intCast(sub_row * 2 + sub_col); │ │559 } │ │560 } │ │566 - const cp = sextantCodepoint(pattern); │ │567 - var utf8_buf: [4]u8 = undefined; │ │568 - const len = std.unicode.utf8Encode(cp, &utf8_buf) catch unreachable; │ │569 - rows[text_row] = rows[text_row] ++ utf8_buf[0..len]; │ │561 } │ │562 + const cp = sextantCodepoint(pattern); │ │563 + var utf8_buf: [4]u8 = undefined; │ │564 + const len = try std.unicode.utf8Encode(cp, &utf8_buf); │ │565 + try rows[text_row].appendSlice(allocator, utf8_buf[0..len]); │ │566 } │ │567 } │ │573 - return rows[0] ++ "\n" ++ rows[1] ++ "\n" ++ rows[2]; │ │568 } │ │569 + │ │570 + var out: std.ArrayList(u8) = .empty; │ │571 + errdefer out.deinit(allocator); │ │572 + try out.appendSlice(allocator, rows[0].items); │ │573 + try out.append(allocator, '\n'); │ │574 + try out.appendSlice(allocator, rows[1].items); │ │575 + try out.append(allocator, '\n'); │ │576 + try out.appendSlice(allocator, rows[2].items); │ │577 + return out.toOwnedSlice(allocator); │ │578 } │ │579 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯