From 760272269f750bcb1cd69b4a450cc02b0ade922e Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 8 Apr 2026 16:39:39 -0500 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=90=9B=20accept=20signed=20int32=20co?= =?UTF-8?q?lors=20in=20validator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rgba() function returns signed int32 when alpha >= 128 (bit 31 set). The validator's u32 type rejected these negative values. Add a color type that accepts the full signed-to-unsigned 32-bit range. --- validate.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/validate.ts b/validate.ts index d3b7d45..f652dea 100644 --- a/validate.ts +++ b/validate.ts @@ -8,6 +8,7 @@ import type { RenderOptions, RenderResult, Term } from "./term.ts"; const u8 = Type.Integer({ minimum: 0, maximum: 255 }); const u16 = Type.Integer({ minimum: 0, maximum: 65535 }); const u32 = Type.Integer({ minimum: 0, maximum: 0xFFFFFFFF }); +const color = Type.Integer({ minimum: -0x80000000, maximum: 0xFFFFFFFF }); /* ── Sizing axis (discriminated union) ────────────────────────────── */ @@ -64,7 +65,7 @@ const CornerRadius = Type.Object({ }); const Border = Type.Object({ - color: u32, + color: color, left: Type.Optional(u8), right: Type.Optional(u8), top: Type.Optional(u8), @@ -93,7 +94,7 @@ const OpenElement = Type.Object({ id: Type.Literal(0x02), name: Type.String(), layout: Type.Optional(Layout), - bg: Type.Optional(u32), + bg: Type.Optional(color), cornerRadius: Type.Optional(CornerRadius), border: Type.Optional(Border), clip: Type.Optional(Clip), @@ -103,7 +104,7 @@ const OpenElement = Type.Object({ const TextOp = Type.Object({ id: Type.Literal(0x03), content: Type.String(), - color: Type.Optional(u32), + color: Type.Optional(color), fontSize: Type.Optional(u8), fontId: Type.Optional(u8), wrap: Type.Optional(u8), From 92577d48369f64b671cfb5dc22b4c2b84a21fa3e Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 8 Apr 2026 16:39:39 -0500 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=90=9B=20accept=20signed=20int32=20co?= =?UTF-8?q?lors=20in=20validator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rgba() function returns signed int32 when alpha >= 128 (bit 31 set). The validator's u32 type rejected these negative values. Add a color type that accepts the full signed-to-unsigned 32-bit range. --- validate.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/validate.ts b/validate.ts index f652dea..1481aa4 100644 --- a/validate.ts +++ b/validate.ts @@ -7,8 +7,11 @@ import type { RenderOptions, RenderResult, Term } from "./term.ts"; const u8 = Type.Integer({ minimum: 0, maximum: 255 }); const u16 = Type.Integer({ minimum: 0, maximum: 65535 }); -const u32 = Type.Integer({ minimum: 0, maximum: 0xFFFFFFFF }); -const color = Type.Integer({ minimum: -0x80000000, maximum: 0xFFFFFFFF }); + +/* RGBA color packed as (a << 24 | r << 16 | g << 8 | b). When alpha >= 128, + * bit 31 is set and JavaScript interprets the value as a negative int32. + * Accept both signed and unsigned representations of the same bit pattern. */ +const rgba = Type.Integer({ minimum: -0x80000000, maximum: 0xFFFFFFFF }); /* ── Sizing axis (discriminated union) ────────────────────────────── */ @@ -65,7 +68,7 @@ const CornerRadius = Type.Object({ }); const Border = Type.Object({ - color: color, + color: rgba, left: Type.Optional(u8), right: Type.Optional(u8), top: Type.Optional(u8), @@ -94,7 +97,7 @@ const OpenElement = Type.Object({ id: Type.Literal(0x02), name: Type.String(), layout: Type.Optional(Layout), - bg: Type.Optional(color), + bg: Type.Optional(rgba), cornerRadius: Type.Optional(CornerRadius), border: Type.Optional(Border), clip: Type.Optional(Clip), @@ -104,7 +107,7 @@ const OpenElement = Type.Object({ const TextOp = Type.Object({ id: Type.Literal(0x03), content: Type.String(), - color: Type.Optional(color), + color: Type.Optional(rgba), fontSize: Type.Optional(u8), fontId: Type.Optional(u8), wrap: Type.Optional(u8),