diff --git a/src/matrix_harness.zig b/src/matrix_harness.zig index 782b1e4..f89b36a 100644 --- a/src/matrix_harness.zig +++ b/src/matrix_harness.zig @@ -201,6 +201,8 @@ const PROGRESS_ARG_CHARS = 120; const PROGRESS_RET_CHARS = 400; /// Char cap for the bubble body (edits re-send the full body every time). const PROGRESS_MAX_CHARS = 3500; +/// Edit throttling: Element X chokes on long m.replace edit chains. +const PROGRESS_MAX_EDITS = 12; const Progress = struct { alloc: std.mem.Allocator, @@ -212,6 +214,8 @@ const Progress = struct { reply_event_id: ?[]u8 = null, // the streamed-reply message (edited live) reply: std.ArrayListUnmanaged(u8) = .empty, // accumulated reply text replied: bool = false, // true once the full reply was edited in + last_edit_ms: i64 = 0, + edits_done: usize = 0, fn deinit(self: *Progress) void { if (self.event_id) |e| self.alloc.free(e); @@ -309,6 +313,14 @@ const Progress = struct { } fn push(self: *Progress) void { + // Throttle: Element X chokes on long m.replace edit chains, so cap + // total edits per bubble and rate-limit to ~1 per 8 seconds. The + // final state is always flushed by the caller (flush()). + const now_ms: i64 = @intCast(@divTrunc(std.Io.Clock.now(.real, self.io).nanoseconds, std.time.ns_per_ms)); + if (self.edits_done >= PROGRESS_MAX_EDITS) return; + if (now_ms - self.last_edit_ms < 8000 and self.edits_done > 0) return; + self.last_edit_ms = now_ms; + self.edits_done += 1; const id = self.event_id orelse { std.debug.print("DBG push: no event_id, blocks={d}\n", .{self.blocks.items.len}); return;