From 6784388a4239fa85cb1bc3c5dd1766a292021c8b Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Thu, 20 Nov 2025 20:05:01 +0300 Subject: [PATCH 01/10] initial `Blob` support on zigdom --- src/browser/js/bridge.zig | 2 ++ src/browser/webapi/File.zig | 44 ++++++++++++++++++++++++++++++++ src/browser/webapi/file/Blob.zig | 39 ++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 src/browser/webapi/File.zig create mode 100644 src/browser/webapi/file/Blob.zig diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 4319df251..76fcc7bea 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -564,4 +564,6 @@ pub const JsApis = flattenTypes(&.{ @import("../webapi/IntersectionObserver.zig"), @import("../webapi/CustomElementRegistry.zig"), @import("../webapi/ResizeObserver.zig"), + @import("../webapi/file/Blob.zig"), + @import("../webapi/File.zig"), }); diff --git a/src/browser/webapi/File.zig b/src/browser/webapi/File.zig new file mode 100644 index 000000000..646145310 --- /dev/null +++ b/src/browser/webapi/File.zig @@ -0,0 +1,44 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); + +const Blob = @import("file/Blob.zig"); +const js = @import("../js/js.zig"); + +const File = @This(); + +/// `File` inherits `Blob`. +_proto: *Blob, + +// TODO: Implement File API. +pub fn init() File { + return .{ ._proto = undefined }; +} + +pub const JsApi = struct { + pub const bridge = js.Bridge(File); + + pub const Meta = struct { + pub const name = "File"; + pub const prototype_chain = bridge.prototypeChain(); + pub var class_id: bridge.ClassId = undefined; + }; + + pub const constructor = bridge.constructor(File.init, .{}); +}; diff --git a/src/browser/webapi/file/Blob.zig b/src/browser/webapi/file/Blob.zig new file mode 100644 index 000000000..f7c4bb10a --- /dev/null +++ b/src/browser/webapi/file/Blob.zig @@ -0,0 +1,39 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); + +const js = @import("../../js/js.zig"); + +const Blob = @This(); + +pub fn init() Blob { + return .{}; +} + +pub const JsApi = struct { + pub const bridge = js.Bridge(Blob); + + pub const Meta = struct { + pub const name = "Blob"; + pub const prototype_chain = bridge.prototypeChain(); + pub var class_id: bridge.ClassId = undefined; + }; + + pub const constructor = bridge.constructor(Blob.init, .{}); +}; From 20cbf99cdf31e7702950c847b84f43d7600e25b0 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 11:25:41 +0300 Subject: [PATCH 02/10] port `Blob` functions --- src/browser/webapi/file/Blob.zig | 254 ++++++++++++++++++++++++++++++- 1 file changed, 252 insertions(+), 2 deletions(-) diff --git a/src/browser/webapi/file/Blob.zig b/src/browser/webapi/file/Blob.zig index f7c4bb10a..e12408fe5 100644 --- a/src/browser/webapi/file/Blob.zig +++ b/src/browser/webapi/file/Blob.zig @@ -17,13 +17,258 @@ // along with this program. If not, see . const std = @import("std"); +const Writer = std.Io.Writer; const js = @import("../../js/js.zig"); +const Page = @import("../../Page.zig"); +/// https://w3c.github.io/FileAPI/#blob-section +/// https://developer.mozilla.org/en-US/docs/Web/API/Blob const Blob = @This(); -pub fn init() Blob { - return .{}; +/// Immutable slice of blob. +/// Note that another blob may hold a pointer/slice to this, +/// so its better to leave the deallocation of it to arena allocator. +slice: []const u8, +/// MIME attached to blob. Can be an empty string. +mime: []const u8, + +const InitOptions = struct { + /// MIME type. + type: []const u8 = "", + /// How to handle line endings (CR and LF). + /// `transparent` means do nothing, `native` expects CRLF (\r\n) on Windows. + endings: []const u8 = "transparent", +}; + +/// Creates a new Blob. +pub fn init( + maybe_blob_parts: ?[]const []const u8, + maybe_options: ?InitOptions, + page: *Page, +) !*Blob { + const options: InitOptions = maybe_options orelse .{}; + // Setup MIME; This can be any string according to my observations. + const mime: []const u8 = blk: { + const t = options.type; + if (t.len == 0) { + break :blk ""; + } + + break :blk try page.arena.dupe(u8, t); + }; + + const slice = blk: { + if (maybe_blob_parts) |blob_parts| { + var w: Writer.Allocating = .init(page.arena); + const use_native_endings = std.mem.eql(u8, options.endings, "native"); + try writeBlobParts(&w.writer, blob_parts, use_native_endings); + + break :blk w.written(); + } + + break :blk ""; + }; + + return page._factory.create(Blob{ .slice = slice, .mime = mime }); +} + +const largest_vector = @max(std.simd.suggestVectorLength(u8) orelse 1, 8); +/// Array of possible vector sizes for the current arch in decrementing order. +/// We may move this to some file for SIMD helpers in the future. +const vector_sizes = blk: { + // Required for length calculation. + var n: usize = largest_vector; + var total: usize = 0; + while (n != 2) : (n /= 2) total += 1; + // Populate an array with vector sizes. + n = largest_vector; + var i: usize = 0; + var items: [total]usize = undefined; + while (n != 2) : (n /= 2) { + defer i += 1; + items[i] = n; + } + + break :blk items; +}; + +/// Writes blob parts to given `Writer` with desired endings. +fn writeBlobParts( + writer: *Writer, + blob_parts: []const []const u8, + use_native_endings: bool, +) !void { + // Transparent. + if (!use_native_endings) { + for (blob_parts) |part| { + try writer.writeAll(part); + } + + return; + } + + // TODO: Windows support. + + // Linux & Unix. + // Both Firefox and Chrome implement it as such: + // CRLF => LF + // CR => LF + // So even though CR is not followed by LF, it gets replaced. + // + // I believe this is because such scenario is possible: + // ``` + // let parts = [ "the quick\r", "\nbrown fox" ]; + // ``` + // In the example, one should have to check the part before in order to + // understand that CRLF is being presented in the final buffer. + // So they took a simpler approach, here's what given blob parts produce: + // ``` + // "the quick\n\nbrown fox" + // ``` + scan_parts: for (blob_parts) |part| { + var end: usize = 0; + + inline for (vector_sizes) |vector_len| { + const Vec = @Vector(vector_len, u8); + + while (end + vector_len <= part.len) : (end += vector_len) { + const cr: Vec = @splat('\r'); + // Load chunk as vectors. + const slice = part[end..][0..vector_len]; + const chunk: Vec = slice.*; + // Look for CR. + const match = chunk == cr; + + // Create a bitset out of match vector. + const bitset = std.bit_set.IntegerBitSet(vector_len){ + .mask = @bitCast(@intFromBool(match)), + }; + + var iter = bitset.iterator(.{}); + var relative_start: usize = 0; + while (iter.next()) |index| { + _ = try writer.writeVec(&.{ slice[relative_start..index], "\n" }); + + if (index + 1 != slice.len and slice[index + 1] == '\n') { + relative_start = index + 2; + } else { + relative_start = index + 1; + } + } + + _ = try writer.writeVec(&.{slice[relative_start..]}); + } + } + + // Scalar scan fallback. + var relative_start: usize = end; + while (end < part.len) { + if (part[end] == '\r') { + _ = try writer.writeVec(&.{ part[relative_start..end], "\n" }); + + // Part ends with CR. We can continue to next part. + if (end + 1 == part.len) { + continue :scan_parts; + } + + // If next char is LF, skip it too. + if (part[end + 1] == '\n') { + relative_start = end + 2; + } else { + relative_start = end + 1; + } + } + + end += 1; + } + + // Write the remaining. We get this in such situations: + // `the quick brown\rfox` + // `the quick brown\r\nfox` + try writer.writeAll(part[relative_start..end]); + } +} + +/// Returns a Promise that resolves with the contents of the blob +/// as binary data contained in an ArrayBuffer. +//pub fn arrayBuffer(self: *const Blob, page: *Page) !js.Promise { +// return page.js.resolvePromise(js.ArrayBuffer{ .values = self.slice }); +//} + +// TODO: Implement `stream`; requires `ReadableStream`. + +/// Returns a Promise that resolves with a string containing +/// the contents of the blob, interpreted as UTF-8. +pub fn text(self: *const Blob, page: *Page) !js.Promise { + return page.js.resolvePromise(self.slice); +} + +/// Extension to Blob; works on Firefox and Safari. +/// https://developer.mozilla.org/en-US/docs/Web/API/Blob/bytes +/// Returns a Promise that resolves with a Uint8Array containing +/// the contents of the blob as an array of bytes. +pub fn bytes(self: *const Blob, page: *Page) !js.Promise { + return page.js.resolvePromise(js.TypedArray(u8){ .values = self.slice }); +} + +/// Returns a new Blob object which contains data +/// from a subset of the blob on which it's called. +pub fn getSlice( + self: *const Blob, + maybe_start: ?i32, + maybe_end: ?i32, + maybe_content_type: ?[]const u8, + page: *Page, +) !Blob { + const mime: []const u8 = blk: { + if (maybe_content_type) |content_type| { + if (content_type.len == 0) { + break :blk ""; + } + + break :blk try page.arena.dupe(u8, content_type); + } + + break :blk ""; + }; + + const slice = self.slice; + if (maybe_start) |_start| { + const start = blk: { + if (_start < 0) { + break :blk slice.len -| @abs(_start); + } + + break :blk @min(slice.len, @as(u31, @intCast(_start))); + }; + + const end: usize = blk: { + if (maybe_end) |_end| { + if (_end < 0) { + break :blk @max(start, slice.len -| @abs(_end)); + } + + break :blk @min(slice.len, @max(start, @as(u31, @intCast(_end)))); + } + + break :blk slice.len; + }; + + return .{ .slice = slice[start..end], .mime = mime }; + } + + return .{ .slice = slice, .mime = mime }; +} + +/// Returns the size of the Blob in bytes. +pub fn size(self: *const Blob) usize { + return self.slice.len; +} + +/// Returns the type of Blob; likely a MIME type, yet anything can be given. +pub fn @"type"(self: *const Blob) []const u8 { + return self.mime; } pub const JsApi = struct { @@ -36,4 +281,9 @@ pub const JsApi = struct { }; pub const constructor = bridge.constructor(Blob.init, .{}); + pub const text = bridge.function(Blob.text, .{}); + pub const bytes = bridge.function(Blob.bytes, .{}); + pub const slice = bridge.function(Blob.getSlice, .{}); + pub const size = bridge.accessor(Blob.size, null, .{}); + pub const @"type" = bridge.accessor(Blob.type, null, .{}); }; From 4d192f5930cce5af88a8dadec8d45d8e5f624315 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 11:40:33 +0300 Subject: [PATCH 03/10] port `File` API tests --- src/browser/tests/blob.html | 107 +++++++++++++++++++++++++++++++ src/browser/tests/file.html | 12 ++++ src/browser/webapi/File.zig | 5 ++ src/browser/webapi/file/Blob.zig | 5 ++ 4 files changed, 129 insertions(+) create mode 100644 src/browser/tests/blob.html create mode 100644 src/browser/tests/file.html diff --git a/src/browser/tests/blob.html b/src/browser/tests/blob.html new file mode 100644 index 000000000..693095c1f --- /dev/null +++ b/src/browser/tests/blob.html @@ -0,0 +1,107 @@ + + + Test Document Title + + + + + + + + + diff --git a/src/browser/tests/file.html b/src/browser/tests/file.html new file mode 100644 index 000000000..3db5fdfee --- /dev/null +++ b/src/browser/tests/file.html @@ -0,0 +1,12 @@ + + + Test Document Title + + + + diff --git a/src/browser/webapi/File.zig b/src/browser/webapi/File.zig index 646145310..0c776abbf 100644 --- a/src/browser/webapi/File.zig +++ b/src/browser/webapi/File.zig @@ -42,3 +42,8 @@ pub const JsApi = struct { pub const constructor = bridge.constructor(File.init, .{}); }; + +const testing = @import("../../testing.zig"); +test "WebApi: File" { + try testing.htmlRunner("file.html", .{}); +} diff --git a/src/browser/webapi/file/Blob.zig b/src/browser/webapi/file/Blob.zig index e12408fe5..bc15e1cff 100644 --- a/src/browser/webapi/file/Blob.zig +++ b/src/browser/webapi/file/Blob.zig @@ -287,3 +287,8 @@ pub const JsApi = struct { pub const size = bridge.accessor(Blob.size, null, .{}); pub const @"type" = bridge.accessor(Blob.type, null, .{}); }; + +const testing = @import("../../../testing.zig"); +test "WebApi: Blob" { + try testing.htmlRunner("blob.html", .{}); +} From f4d58c8823e30ef828a31263083f96fedc58eb56 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 14:14:21 +0300 Subject: [PATCH 04/10] prefer `get` prefix in getter accessors --- src/browser/webapi/file/Blob.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/browser/webapi/file/Blob.zig b/src/browser/webapi/file/Blob.zig index bc15e1cff..c4e38725e 100644 --- a/src/browser/webapi/file/Blob.zig +++ b/src/browser/webapi/file/Blob.zig @@ -262,12 +262,12 @@ pub fn getSlice( } /// Returns the size of the Blob in bytes. -pub fn size(self: *const Blob) usize { +pub fn getSize(self: *const Blob) usize { return self.slice.len; } /// Returns the type of Blob; likely a MIME type, yet anything can be given. -pub fn @"type"(self: *const Blob) []const u8 { +pub fn getType(self: *const Blob) []const u8 { return self.mime; } @@ -284,8 +284,8 @@ pub const JsApi = struct { pub const text = bridge.function(Blob.text, .{}); pub const bytes = bridge.function(Blob.bytes, .{}); pub const slice = bridge.function(Blob.getSlice, .{}); - pub const size = bridge.accessor(Blob.size, null, .{}); - pub const @"type" = bridge.accessor(Blob.type, null, .{}); + pub const size = bridge.accessor(Blob.getSize, null, .{}); + pub const @"type" = bridge.accessor(Blob.getType, null, .{}); }; const testing = @import("../../../testing.zig"); From 3e44d5bfdfebd581f791dcda76580b99b852fc1f Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 14:22:52 +0300 Subject: [PATCH 05/10] move `Blob` out of `files/` + provide subclasses of `Blob` in `_type` --- src/browser/webapi/{file => }/Blob.zig | 6 ++++++ 1 file changed, 6 insertions(+) rename src/browser/webapi/{file => }/Blob.zig (99%) diff --git a/src/browser/webapi/file/Blob.zig b/src/browser/webapi/Blob.zig similarity index 99% rename from src/browser/webapi/file/Blob.zig rename to src/browser/webapi/Blob.zig index c4e38725e..5e2f2e9d8 100644 --- a/src/browser/webapi/file/Blob.zig +++ b/src/browser/webapi/Blob.zig @@ -26,6 +26,7 @@ const Page = @import("../../Page.zig"); /// https://developer.mozilla.org/en-US/docs/Web/API/Blob const Blob = @This(); +_type: Type, /// Immutable slice of blob. /// Note that another blob may hold a pointer/slice to this, /// so its better to leave the deallocation of it to arena allocator. @@ -33,6 +34,11 @@ slice: []const u8, /// MIME attached to blob. Can be an empty string. mime: []const u8, +const Type = union(enum) { + generic, + file: *@import("File.zig"), +}; + const InitOptions = struct { /// MIME type. type: []const u8 = "", From 9a7bafb02c45e126d7d07a97a44e0e88b8a56b66 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 14:47:18 +0300 Subject: [PATCH 06/10] `Blob` is in another castle --- src/browser/js/bridge.zig | 3 +-- src/browser/webapi/Blob.zig | 6 +++--- src/browser/webapi/File.zig | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 76fcc7bea..b1a650b86 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -563,7 +563,6 @@ pub const JsApis = flattenTypes(&.{ @import("../webapi/MutationObserver.zig"), @import("../webapi/IntersectionObserver.zig"), @import("../webapi/CustomElementRegistry.zig"), - @import("../webapi/ResizeObserver.zig"), - @import("../webapi/file/Blob.zig"), + @import("../webapi/Blob.zig"), @import("../webapi/File.zig"), }); diff --git a/src/browser/webapi/Blob.zig b/src/browser/webapi/Blob.zig index 5e2f2e9d8..19ee09632 100644 --- a/src/browser/webapi/Blob.zig +++ b/src/browser/webapi/Blob.zig @@ -19,8 +19,8 @@ const std = @import("std"); const Writer = std.Io.Writer; -const js = @import("../../js/js.zig"); -const Page = @import("../../Page.zig"); +const js = @import("../js/js.zig"); +const Page = @import("../Page.zig"); /// https://w3c.github.io/FileAPI/#blob-section /// https://developer.mozilla.org/en-US/docs/Web/API/Blob @@ -294,7 +294,7 @@ pub const JsApi = struct { pub const @"type" = bridge.accessor(Blob.getType, null, .{}); }; -const testing = @import("../../../testing.zig"); +const testing = @import("../../testing.zig"); test "WebApi: Blob" { try testing.htmlRunner("blob.html", .{}); } diff --git a/src/browser/webapi/File.zig b/src/browser/webapi/File.zig index 0c776abbf..980dd5cc0 100644 --- a/src/browser/webapi/File.zig +++ b/src/browser/webapi/File.zig @@ -18,7 +18,8 @@ const std = @import("std"); -const Blob = @import("file/Blob.zig"); +const Page = @import("../Page.zig"); +const Blob = @import("Blob.zig"); const js = @import("../js/js.zig"); const File = @This(); From b4f9f968f628cb21143a88a6d4f41c8f5eebce07 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 14:50:23 +0300 Subject: [PATCH 07/10] add `Blob` ancestor initializer to `Factory` --- src/browser/Factory.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/browser/Factory.zig b/src/browser/Factory.zig index 1f3e45bad..696ae98c3 100644 --- a/src/browser/Factory.zig +++ b/src/browser/Factory.zig @@ -31,6 +31,7 @@ const Element = @import("webapi/Element.zig"); const Document = @import("webapi/Document.zig"); const EventTarget = @import("webapi/EventTarget.zig"); const XMLHttpRequestEventTarget = @import("webapi/net/XMLHttpRequestEventTarget.zig"); +const Blob = @import("webapi/Blob.zig"); const MemoryPoolAligned = std.heap.MemoryPoolAligned; @@ -224,6 +225,20 @@ pub fn xhrEventTarget(self: *Factory, child: anytype) !*@TypeOf(child) { return child_ptr; } +pub fn blob(self: *Factory, child: anytype) !*@TypeOf(child) { + const child_ptr = try self.createT(@TypeOf(child)); + child_ptr.* = child; + + const b = try self.createT(Blob); + child_ptr._proto = b; + b.* = .{ + ._type = unionInit(Blob.Type, child_ptr), + .slice = "", + .mime = "", + }; + return child_ptr; +} + pub fn create(self: *Factory, value: anytype) !*@TypeOf(value) { const ptr = try self.createT(@TypeOf(value)); ptr.* = value; From 0142520bb80c270181bae4aca019485f88239d3d Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 14:51:50 +0300 Subject: [PATCH 08/10] change how `Blob` and `File` initialized --- src/browser/webapi/Blob.zig | 8 ++++++-- src/browser/webapi/File.zig | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/browser/webapi/Blob.zig b/src/browser/webapi/Blob.zig index 19ee09632..43c78b32b 100644 --- a/src/browser/webapi/Blob.zig +++ b/src/browser/webapi/Blob.zig @@ -34,7 +34,7 @@ slice: []const u8, /// MIME attached to blob. Can be an empty string. mime: []const u8, -const Type = union(enum) { +pub const Type = union(enum) { generic, file: *@import("File.zig"), }; @@ -76,7 +76,11 @@ pub fn init( break :blk ""; }; - return page._factory.create(Blob{ .slice = slice, .mime = mime }); + return page._factory.create(Blob{ + ._type = .generic, + .slice = slice, + .mime = mime, + }); } const largest_vector = @max(std.simd.suggestVectorLength(u8) orelse 1, 8); diff --git a/src/browser/webapi/File.zig b/src/browser/webapi/File.zig index 980dd5cc0..a67a8a6f4 100644 --- a/src/browser/webapi/File.zig +++ b/src/browser/webapi/File.zig @@ -28,8 +28,8 @@ const File = @This(); _proto: *Blob, // TODO: Implement File API. -pub fn init() File { - return .{ ._proto = undefined }; +pub fn init(page: *Page) !*File { + return page._factory.blob(File{ ._proto = undefined }); } pub const JsApi = struct { From 5c9ff9d1a21f05bf80b030e6896f28f3dc970322 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 14:52:39 +0300 Subject: [PATCH 09/10] fix `Blob#slice` return type --- src/browser/webapi/Blob.zig | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/browser/webapi/Blob.zig b/src/browser/webapi/Blob.zig index 43c78b32b..9abe6f295 100644 --- a/src/browser/webapi/Blob.zig +++ b/src/browser/webapi/Blob.zig @@ -230,7 +230,7 @@ pub fn getSlice( maybe_end: ?i32, maybe_content_type: ?[]const u8, page: *Page, -) !Blob { +) !*Blob { const mime: []const u8 = blk: { if (maybe_content_type) |content_type| { if (content_type.len == 0) { @@ -265,10 +265,18 @@ pub fn getSlice( break :blk slice.len; }; - return .{ .slice = slice[start..end], .mime = mime }; + return page._factory.create(Blob{ + ._type = .generic, + .slice = slice[start..end], + .mime = mime, + }); } - return .{ .slice = slice, .mime = mime }; + return page._factory.create(Blob{ + ._type = .generic, + .slice = slice, + .mime = mime, + }); } /// Returns the size of the Blob in bytes. From de9a0c0166d44c28b1182ca389eb05e1d019f452 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Fri, 21 Nov 2025 14:59:29 +0300 Subject: [PATCH 10/10] bring back import for `ResizeObserver` No idea how I removed this single line while rebasing... --- src/browser/js/bridge.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index b1a650b86..f9ba64f6e 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -563,6 +563,7 @@ pub const JsApis = flattenTypes(&.{ @import("../webapi/MutationObserver.zig"), @import("../webapi/IntersectionObserver.zig"), @import("../webapi/CustomElementRegistry.zig"), + @import("../webapi/ResizeObserver.zig"), @import("../webapi/Blob.zig"), @import("../webapi/File.zig"), });