╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────╮ │311 var root = try ui.initRoot(allocator, &snapshot.page, &session);│ │312 defer root.deinit(allocator); │ │313 │ │314 - const content = try generateHtml(allocator, &root, &session); │ │314 + const content = try generateHtml(allocator, &root); │ │315 defer allocator.free(content); │ │316 const overlay = try generateOverlay(allocator, &root); │ │317 defer allocator.free(overlay); │ ╰─────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │367 } │ │368 │ │369 // emits the TUI grid cells (one <span> run per focusable widget) │ │370 -pub fn generateHtml(allocator: std.mem.Allocator, root: *ui.Widget, session: *const ui.Session) ![]const u8 { │ │371 - _ = session; │ │370 +pub fn generateHtml(allocator: std.mem.Allocator, root: *ui.Widget) ![]const u8 { │ │371 const grid = root.getGrid() orelse return error.MissingGrid; │ │372 const root_focus = root.getFocus(); │ │373 + │ │374 + const Tag = union(enum) { │ │375 + span, │ │376 + a: []const u8, │ │377 + │ │378 + const a_prefix = "a:"; │ │379 + │ │380 + fn init(str: []const u8) @This() { │ │381 + if (std.mem.startsWith(u8, str, a_prefix)) { │ │382 + return .{ .a = str[a_prefix.len..] }; │ │383 + } │ │384 + return .span; │ │385 + } │ │386 + │ │387 + fn writeOpenTag(self: @This(), alloc: std.mem.Allocator, out: *std.ArrayList(u8), id: usize) !void { │ │388 + switch (self) { │ │389 + .span => { │ │390 + var buf: [128]u8 = undefined; │ │391 + const open = try std.fmt.bufPrint(&buf, "<span class=\"clickable\" data-focus-id=\"{d}\">", .{id});│ │392 + try out.appendSlice(alloc, open); │ │393 + }, │ │394 + .a => |href| { │ │395 + try out.appendSlice(alloc, "<a class=\"clickable\" data-focus-id=\""); │ │396 + var id_buf: [32]u8 = undefined; │ │397 + try out.appendSlice(alloc, try std.fmt.bufPrint(&id_buf, "{d}", .{id})); │ │398 + try out.appendSlice(alloc, "\" href=\""); │ │399 + try appendEscapedHtml(alloc, out, href); │ │400 + try out.appendSlice(alloc, "\">"); │ │401 + }, │ │402 + } │ │403 + } │ │404 │ │405 + fn closeTag(self: @This()) []const u8 { │ │406 + return switch (self) { │ │407 + .span => "</span>", │ │408 + .a => "</a>", │ │409 + }; │ │410 + } │ │411 + }; │ │412 + │ │413 var out: std.ArrayList(u8) = .empty; │ │414 errdefer out.deinit(allocator); │ │415 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │416 for (0..grid.size.height) |y| { │ │379 - var current_id: ?usize = null; │ │417 + var current_id_maybe: ?usize = null; │ │418 + var current_tag_maybe: ?Tag = null; │ │419 for (0..grid.size.width) |x| { │ │420 const cell_id = cellFocusId(root_focus, x, y); │ │382 - if (cell_id != current_id) { │ │383 - if (current_id != null) try out.appendSlice(allocator, "</span>"); │ │421 + if (cell_id != current_id_maybe) { │ │422 + if (current_tag_maybe) |current_tag| try out.appendSlice(allocator, current_tag.closeTag()); │ │423 + current_tag_maybe = null; │ │424 + │ │425 if (cell_id) |id| { │ │385 - var buf: [128]u8 = undefined; │ │386 - const tag = try std.fmt.bufPrint(&buf, "<span class=\"clickable\" data-focus-id=\"{d}\">", .{id});│ │387 - try out.appendSlice(allocator, tag); │ │426 + current_tag_maybe = if (root_focus.children.get(id)) |child| switch (child.focus.kind) { │ │427 + .custom => |custom| .init(custom), │ │428 + else => .span, │ │429 + } else null; │ │430 + │ │431 + if (current_tag_maybe) |current_tag| { │ │432 + try current_tag.writeOpenTag(allocator, &out, id); │ │433 + } │ │434 } │ │389 - current_id = cell_id; │ │435 + current_id_maybe = cell_id; │ │436 } │ │437 const rune = grid.cells.items[try grid.cells.at(.{ y, x })].rune orelse " "; │ │438 try appendEscapedHtml(allocator, &out, rune); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │439 } │ │394 - if (current_id != null) try out.appendSlice(allocator, "</span>"); │ │440 + if (current_tag_maybe) |current_tag| try out.appendSlice(allocator, current_tag.closeTag());│ │441 try out.append(allocator, '\n'); │ │442 } │ │443 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯