╭ changed ───────────╮ │src/ui/Repo/Undo.zig│ ╰────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │178 // the repository; the web handler uses its matching http authorization path. │ │179 pub fn perform(allocator: std.mem.Allocator, session: *ui.Session, target: ui.RoutablePage.RepoUndoRoute) !void {│ │180 const identity = target.name.slice(); │ │181 + const index = if (target.clear) null else target.index orelse return error.InvalidHistoryIndex; │ │182 const source = try authorizedSource(session, identity) orelse return error.Forbidden; │ │183 const io = session.io orelse return error.NotFound; │ │183 - if (target.clear) │ │184 - try clearHistory(io, allocator, source) │ │185 - else │ │186 - try execute(io, allocator, source, target.index orelse return error.InvalidHistoryIndex); │ │184 + if (index) |value| try execute(io, allocator, source, value) else try clearHistory(io, allocator, source); │ │185 try session.navigate(ui.RoutablePage.repoUndoRoute(identity, null) orelse return error.RouteTooLong); │ │186 } │ │187 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │245 try addText(allocator, &rows, "next →", try std.fmt.allocPrint(session.page_arena.allocator(), "a:{s}", .{try route.toUrl(session.page_arena)}), .hidden);│ │246 } │ │247 if (data.items.len == 0) try addText(allocator, &rows, "no undo history", null, .hidden); │ │250 - if (rows.children.count() > 0) rows.getFocus().child_id = rows.children.keys()[0]; │ │248 + rows.getFocus().child_id = rows.children.keys()[0]; │ │249 break :blk try wgt.Scroll(ui.Widget).init(allocator, .{ .box = rows }, .{ .direction = .vert, .web_native = !session.is_terminal, .fill = true }); │ │250 }; │ │251 errdefer scroll.deinit(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │302 row.getFocus().mode = .all; │ │303 if (link) |value| row.getFocus().kind = .{ .custom = value }; │ │304 try box.children.put(allocator, row.getFocus().id, .{ .widget = .{ .text_box = row }, .rect = null, .min_size = null });│ │305 + } │ │306 + │ │307 + // enter counts only on the focused button; a click counts anywhere on it │ │308 + fn activated(root_focus: *Focus, button_id: usize, key: Key) bool { │ │309 + return switch (key) { │ │310 + .enter => root_focus.grandchild_id == button_id, │ │311 + .mouse => |mouse| inp.leftClickOn(root_focus, button_id, mouse), │ │312 + else => false, │ │313 + }; │ │314 + } │ │315 + │ │316 + // the terminal hosts run the action after rendering; the web renderer │ │317 + // posts the form instead, and wasm has no repository to act on │ │318 + fn requestUndo(self: *View, route: ui.RoutablePage) void { │ │319 + if (builtin.target.cpu.arch == .wasm32) return; │ │320 + if (!self.session.is_terminal or self.session.host_request != null) return; │ │321 + self.session.host_request = .{ .undo = route.repo_undo }; │ │322 } │ │323 │ │324 fn headerBox(self: *View) *wgt.Box(ui.Widget) { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │427 if (self.detailActive()) { │ │428 const details = &self.detailScroll().child.box; │ │429 if (self.selected()) |index| { │ │415 - const button_id = details.children.keys()[0]; │ │416 - const activated = switch (key) { │ │417 - .enter => root_focus.grandchild_id == button_id, │ │418 - .mouse => |mouse| inp.leftClickOn(root_focus, button_id, mouse), │ │419 - else => false, │ │420 - }; │ │421 - if (activated and self.data.can_undo and self.data.items[index].index > 0) { │ │422 - if (builtin.target.cpu.arch != .wasm32 and self.session.is_terminal and self.session.host_request == null) { │ │423 - const route = ui.RoutablePage.repoUndoRoute(self.data.identity, self.data.items[index].index) orelse return error.RouteTooLong;│ │424 - self.session.host_request = .{ .undo = route.repo_undo }; │ │425 - } │ │430 + if (activated(root_focus, details.children.keys()[0], key) and self.data.can_undo and self.data.items[index].index > 0) { │ │431 + self.requestUndo(ui.RoutablePage.repoUndoRoute(self.data.identity, self.data.items[index].index) orelse return error.RouteTooLong);│ │432 return; │ │433 } │ │434 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │447 fn clearInput(self: *View, key: Key, root_focus: *Focus) !void { │ │448 const center = &self.box.children.values()[content_index].widget.center; │ │449 const button_id = center.child.box.getFocus().child_id orelse return; │ │444 - const activated = switch (key) { │ │445 - .enter => root_focus.grandchild_id == button_id, │ │446 - .mouse => |mouse| inp.leftClickOn(root_focus, button_id, mouse), │ │447 - else => false, │ │448 - }; │ │449 - if (!activated or !self.data.can_undo) return; │ │450 - if (builtin.target.cpu.arch != .wasm32 and self.session.is_terminal and self.session.host_request == null) {│ │451 - const route = ui.RoutablePage.repoUndoClearRoute(self.data.identity) orelse return error.RouteTooLong; │ │452 - self.session.host_request = .{ .undo = route.repo_undo }; │ │453 - } │ │450 + if (!activated(root_focus, button_id, key) or !self.data.can_undo) return; │ │451 + self.requestUndo(ui.RoutablePage.repoUndoClearRoute(self.data.identity) orelse return error.RouteTooLong); │ │452 } │ │453 │ │454 fn contentAtTop(self: *View) bool { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯