Skip to content

Commit f6cc880

Browse files
authored
Merge pull request #20 from thefrontside/refactor/rename-name-to-id
Rename Op discriminant to directive, open() name to id
2 parents 059190e + ee7f5bc commit f6cc880

5 files changed

Lines changed: 37 additions & 56 deletions

File tree

ops.ts

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ const PROP_BORDER = 0x08;
1111
const PROP_CLIP = 0x10;
1212
const PROP_FLOATING = 0x20;
1313

14-
/* ── Packing ──────────────────────────────────────────────────────── */
15-
1614
const encoder = new TextEncoder();
1715

1816
function packAxis(view: DataView, offset: number, axis: SizingAxis): number {
@@ -73,18 +71,18 @@ export function pack(
7371
let o = offset;
7472

7573
for (let op of ops) {
76-
switch (op.id) {
74+
switch (op.directive) {
7775
case OP_CLOSE_ELEMENT:
78-
view.setUint32(o, op.id, true);
76+
view.setUint32(o, op.directive, true);
7977
o += 4;
8078
break;
8179

8280
case OP_OPEN_ELEMENT: {
8381
view.setUint32(o, OP_OPEN_ELEMENT, true);
8482
o += 4;
8583

86-
let id = encoder.encode(op.name);
87-
o = packString(view, id, o);
84+
let bytes = encoder.encode(op.id);
85+
o = packString(view, bytes, o);
8886

8987
let mask = 0;
9088
if (op.layout) mask |= PROP_LAYOUT;
@@ -210,15 +208,11 @@ export function pack(
210208
return (o - offset) / 4;
211209
}
212210

213-
/* ── Color ────────────────────────────────────────────────────────── */
214-
215211
export function rgba(r: number, g: number, b: number, a = 255): number {
216212
return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) |
217213
(b & 0xFF);
218214
}
219215

220-
/* ── Sizing axis types ────────────────────────────────────────────── */
221-
222216
export type SizingAxis =
223217
| { type: "fit"; min?: number; max?: number }
224218
| { type: "grow"; min?: number; max?: number }
@@ -241,15 +235,13 @@ export const percent = (value: number): SizingAxis => ({
241235
});
242236
export const fixed = (value: number): SizingAxis => ({ type: "fixed", value });
243237

244-
/* ── Op descriptors ───────────────────────────────────────────────── */
245-
246238
export interface CloseElement {
247-
id: typeof OP_CLOSE_ELEMENT;
239+
directive: typeof OP_CLOSE_ELEMENT;
248240
}
249241

250242
export interface OpenElement {
251-
id: typeof OP_OPEN_ELEMENT;
252-
name: string;
243+
directive: typeof OP_OPEN_ELEMENT;
244+
id: string;
253245
layout?: {
254246
width?: SizingAxis;
255247
height?: SizingAxis;
@@ -280,7 +272,7 @@ export interface OpenElement {
280272
}
281273

282274
export interface Text {
283-
id: typeof OP_TEXT;
275+
directive: typeof OP_TEXT;
284276
content: string;
285277
color?: number;
286278
fontSize?: number;
@@ -291,22 +283,20 @@ export interface Text {
291283

292284
export type Op = OpenElement | Text | CloseElement;
293285

294-
/* ── Descriptor constructors ──────────────────────────────────────── */
295-
296286
export function open(
297-
name: string,
298-
props: Omit<OpenElement, "id" | "name"> = {},
287+
id: string,
288+
props: Omit<OpenElement, "directive" | "id"> = {},
299289
): OpenElement {
300-
return { id: OP_OPEN_ELEMENT, name, ...props };
290+
return { directive: OP_OPEN_ELEMENT, id, ...props };
301291
}
302292

303293
export function text(
304294
content: string,
305-
props: Omit<Text, "id" | "content"> = {},
295+
props: Omit<Text, "directive" | "content"> = {},
306296
): Text {
307-
return { id: OP_TEXT, content, ...props };
297+
return { directive: OP_TEXT, content, ...props };
308298
}
309299

310300
export function close(): CloseElement {
311-
return { id: OP_CLOSE_ELEMENT };
301+
return { directive: OP_CLOSE_ELEMENT };
312302
}

specs/renderer-spec.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,7 @@ stages. The caller MUST NOT need to perform any of these operations.
225225
symmetric: both calls occur within the same render transaction, in the same
226226
function scope.
227227

228-
**INV-7. Element identity disambiguation.** When multiple elements within a
229-
frame share the same id, the renderer MUST disambiguate their identities so that
230-
the layout engine does not conflate them. The disambiguation mechanism is an
231-
implementation detail, but the guarantee is normative: identical ids MUST NOT
232-
cause layout corruption or element conflation.
233-
234-
**INV-8. Separation of concerns.** The rendering concern and the input-parsing
228+
**INV-7. Separation of concerns.** The rendering concern and the input-parsing
235229
concern MUST remain independent. Neither MUST depend on the other's state,
236230
types, or API surface. They MAY share a compiled WASM binary for loading
237231
efficiency, but this is an implementation convenience, not an architectural
@@ -374,7 +368,8 @@ open(id: string, props?): OpenElement
374368

375369
Creates an element-open directive. The `id` parameter provides an identity for
376370
the element within the frame, used by the underlying layout engine for element
377-
tracking and hit-testing. The optional `props` parameter carries configuration
371+
tracking and hit-testing. IDs MUST be unique within a frame; passing duplicate
372+
IDs is undefined behavior. The optional `props` parameter carries configuration
378373
for layout, styling, and behavior.
379374

380375
Elements opened with `open()` MUST be closed with a corresponding `close()`
@@ -473,12 +468,10 @@ transfer mechanism is an implementation detail described in Section 12.1.
473468

474469
### 9.3 Directive identity
475470

476-
Each element directive is assigned an identity within the frame for use by the
477-
underlying layout engine. When multiple elements share the same id (the `id`
478-
parameter to `open()`), the renderer MUST disambiguate their identities
479-
automatically. The disambiguation mechanism is an implementation detail. The
480-
normative requirement is that the caller MUST NOT need to provide globally
481-
unique ids; the renderer handles uniqueness internally.
471+
Each element directive carries an `id` provided by the caller via `open()`.
472+
Element IDs MUST be unique within a frame. The renderer uses the ID directly as
473+
the element's identity for the layout engine. Passing duplicate IDs within a
474+
single frame is undefined behavior.
482475

483476
---
484477

@@ -501,10 +494,9 @@ renderer processes directives in the order they appear in the array.
501494

502495
### 10.3 Element identity within a frame
503496

504-
Within a single frame, each element MUST have an unambiguous identity for the
505-
layout engine. As specified in Section 9.3, the renderer handles disambiguation.
506-
Two elements with the same id in the same frame MUST NOT cause layout
507-
corruption, hash collision, or identity conflation.
497+
Within a single frame, each element MUST have a unique identity for the layout
498+
engine. As specified in Section 9.3, element IDs MUST be unique within a frame.
499+
Passing duplicate IDs is undefined behavior.
508500

509501
### 10.4 No cross-frame identity
510502

src/clayterm.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,6 @@ struct Clayterm *init(void *mem, int w, int h) {
437437

438438
void reduce(struct Clayterm *ct, uint32_t *buf, int len, int mode, int row) {
439439
int i = 0;
440-
uint32_t idx = 0;
441440

442441
Clay_BeginLayout();
443442

@@ -454,7 +453,7 @@ void reduce(struct Clayterm *ct, uint32_t *buf, int len, int mode, int row) {
454453

455454
if (id_len > 0) {
456455
Clay_String str = {.length = (int32_t)id_len, .chars = id_chars};
457-
Clay_ElementId eid = Clay__HashString(str, idx++);
456+
Clay_ElementId eid = Clay__HashString(str, 0);
458457
Clay__OpenElementWithId(eid);
459458
} else {
460459
Clay__OpenElement();

test/validate.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ describe("validate", () => {
1919
expect(validate([])).toBe(true);
2020
});
2121

22-
it("rejects ops with wrong id", () => {
23-
expect(validate([{ id: 0xff }])).toBe(false);
22+
it("rejects ops with wrong directive", () => {
23+
expect(validate([{ directive: 0xff }])).toBe(false);
2424
});
2525

26-
it("rejects open element missing name", () => {
27-
expect(validate([{ id: 0x02 }])).toBe(false);
26+
it("rejects open element missing id", () => {
27+
expect(validate([{ directive: 0x02 }])).toBe(false);
2828
});
2929

3030
it("rejects text missing content", () => {
31-
expect(validate([{ id: 0x03 }])).toBe(false);
31+
expect(validate([{ directive: 0x03 }])).toBe(false);
3232
});
3333

3434
it("rejects non-array", () => {
@@ -40,7 +40,7 @@ describe("validate", () => {
4040
});
4141

4242
it("assert throws TypeError on bad input", () => {
43-
expect(() => assert([{ id: 0x02 }])).toThrow(TypeError);
43+
expect(() => assert([{ directive: 0x02 }])).toThrow(TypeError);
4444
});
4545

4646
it("rejects padding > 255 (u8 overflow)", () => {
@@ -106,6 +106,6 @@ describe("validated", () => {
106106

107107
it("throws on invalid ops", () => {
108108
// deno-lint-ignore no-explicit-any
109-
expect(() => term.render([{ id: 0xff }] as any)).toThrow(TypeError);
109+
expect(() => term.render([{ directive: 0xff }] as any)).toThrow(TypeError);
110110
});
111111
});

validate.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,13 @@ const Floating = Type.Object({
8989
zIndex: Type.Optional(u16),
9090
});
9191

92-
/* ── Op types (discriminated on `id`) ─────────────────────────────── */
92+
/* ── Op types (discriminated on `directive`) ──────────────────────── */
9393

94-
const CloseElement = Type.Object({ id: Type.Literal(0x04) });
94+
const CloseElement = Type.Object({ directive: Type.Literal(0x04) });
9595

9696
const OpenElement = Type.Object({
97-
id: Type.Literal(0x02),
98-
name: Type.String(),
97+
directive: Type.Literal(0x02),
98+
id: Type.String(),
9999
layout: Type.Optional(Layout),
100100
bg: Type.Optional(rgba),
101101
cornerRadius: Type.Optional(CornerRadius),
@@ -105,7 +105,7 @@ const OpenElement = Type.Object({
105105
});
106106

107107
const TextOp = Type.Object({
108-
id: Type.Literal(0x03),
108+
directive: Type.Literal(0x03),
109109
content: Type.String(),
110110
color: Type.Optional(rgba),
111111
fontSize: Type.Optional(u8),

0 commit comments

Comments
 (0)