From d5556fda93a60c2f8320f083d47395a3489a6b35 Mon Sep 17 00:00:00 2001 From: William Chan Date: Sat, 11 Apr 2026 22:38:58 +0800 Subject: [PATCH 1/2] feat(console): implement group, groupCollapsed, and groupEnd Add missing console grouping APIs per the WHATWG Console spec: - console.group(...data): logs label and increments group depth - console.groupCollapsed(...data): same as group (headless mode has no visual collapsing, so behavior is identical to group) - console.groupEnd(): decrements group depth, clamped at 0 Depth is tracked via _group_depth (u32) on the Console struct. Saturation arithmetic (+|=) prevents overflow on runaway group() calls. Fixes TypeError crashes on sites that use console.group* APIs (e.g. React/Vite dev builds). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/browser/tests/console/console.html | 21 +++++++++++++++++++++ src/browser/webapi/Console.zig | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/browser/tests/console/console.html b/src/browser/tests/console/console.html index 7abb533f..33c2fa62 100644 --- a/src/browser/tests/console/console.html +++ b/src/browser/tests/console/console.html @@ -26,3 +26,24 @@ testing.expectEqual(true, true); + + diff --git a/src/browser/webapi/Console.zig b/src/browser/webapi/Console.zig index 06a18331..421b6e31 100644 --- a/src/browser/webapi/Console.zig +++ b/src/browser/webapi/Console.zig @@ -26,6 +26,7 @@ const Console = @This(); _timers: std.StringHashMapUnmanaged(u64) = .{}, _counts: std.StringHashMapUnmanaged(u64) = .{}, +_group_depth: u32 = 0, pub const init: Console = .{}; @@ -128,6 +129,22 @@ pub fn timeEnd(self: *Console, label_: ?[]const u8) void { logger.info(.js, "console.timeEnd", .{ .label = label, .elapsed = elapsed - kv.value }); } +pub fn group(self: *Console, values: []js.Value, page: *Page) void { + logger.info(.js, "console.group", .{ValueWriter{ .page = page, .values = values }}); + self._group_depth +|= 1; +} + +pub fn groupCollapsed(self: *Console, values: []js.Value, page: *Page) void { + logger.info(.js, "console.groupCollapsed", .{ValueWriter{ .page = page, .values = values }}); + self._group_depth +|= 1; +} + +pub fn groupEnd(self: *Console) void { + if (self._group_depth > 0) { + self._group_depth -= 1; + } +} + fn timestamp() u64 { return @import("../../datetime.zig").timestamp(.monotonic); } @@ -188,6 +205,9 @@ pub const JsApi = struct { pub const time = bridge.function(Console.time, .{}); pub const timeLog = bridge.function(Console.timeLog, .{}); pub const timeEnd = bridge.function(Console.timeEnd, .{}); + pub const group = bridge.function(Console.group, .{}); + pub const groupCollapsed = bridge.function(Console.groupCollapsed, .{}); + pub const groupEnd = bridge.function(Console.groupEnd, .{}); }; const testing = @import("../../testing.zig"); From e6fd004767c73bfb4be64d0ca182c61c82a66a52 Mon Sep 17 00:00:00 2001 From: William Chan Date: Sun, 12 Apr 2026 06:51:56 +0800 Subject: [PATCH 2/2] refactor: remove unused _group_depth tracking Since group depth is not used for indentation in headless mode, simplify by removing the field. group/groupCollapsed just log, groupEnd is a no-op. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/browser/webapi/Console.zig | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/browser/webapi/Console.zig b/src/browser/webapi/Console.zig index 421b6e31..036eea04 100644 --- a/src/browser/webapi/Console.zig +++ b/src/browser/webapi/Console.zig @@ -26,7 +26,6 @@ const Console = @This(); _timers: std.StringHashMapUnmanaged(u64) = .{}, _counts: std.StringHashMapUnmanaged(u64) = .{}, -_group_depth: u32 = 0, pub const init: Console = .{}; @@ -129,21 +128,15 @@ pub fn timeEnd(self: *Console, label_: ?[]const u8) void { logger.info(.js, "console.timeEnd", .{ .label = label, .elapsed = elapsed - kv.value }); } -pub fn group(self: *Console, values: []js.Value, page: *Page) void { +pub fn group(_: *const Console, values: []js.Value, page: *Page) void { logger.info(.js, "console.group", .{ValueWriter{ .page = page, .values = values }}); - self._group_depth +|= 1; } -pub fn groupCollapsed(self: *Console, values: []js.Value, page: *Page) void { +pub fn groupCollapsed(_: *const Console, values: []js.Value, page: *Page) void { logger.info(.js, "console.groupCollapsed", .{ValueWriter{ .page = page, .values = values }}); - self._group_depth +|= 1; } -pub fn groupEnd(self: *Console) void { - if (self._group_depth > 0) { - self._group_depth -= 1; - } -} +pub fn groupEnd(_: *const Console) void {} fn timestamp() u64 { return @import("../../datetime.zig").timestamp(.monotonic);