[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay
5

Configure Feed

Select the types of activity you want to include in your feed.

feat: expand floating parameters

Ryan Rauh (May 9, 2026, 8:04 AM EDT) bd2dcc9d 9470772d

+179 -8
+47 -3
ops.ts
··· 163 163 o += 4; 164 164 view.setFloat32(o, f.y ?? 0, true); 165 165 o += 4; 166 + view.setFloat32(o, f.expand?.width ?? 0, true); 167 + o += 4; 168 + view.setFloat32(o, f.expand?.height ?? 0, true); 169 + o += 4; 166 170 view.setUint32(o, f.parent ?? 0, true); 167 171 o += 4; 168 172 view.setUint32( 169 173 o, 170 - (f.attachTo ?? 0) | ((f.attachPoints ?? 0) << 8) | 171 - ((f.zIndex ?? 0) << 16), 174 + (f.attachTo ?? 0) | 175 + ((f.attachPoints?.element ?? 0) << 8) | 176 + ((f.attachPoints?.parent ?? 0) << 16) | 177 + ((f.pointerCaptureMode ?? 0) << 24), 178 + true, 179 + ); 180 + o += 4; 181 + view.setUint32( 182 + o, 183 + (f.clipTo ?? 0) | (((f.zIndex ?? 0) & 0xffff) << 8), 172 184 true, 173 185 ); 174 186 o += 4; ··· 264 276 floating?: { 265 277 x?: number; 266 278 y?: number; 279 + expand?: { width?: number; height?: number }; 267 280 parent?: number; 268 281 attachTo?: number; 269 - attachPoints?: number; 282 + attachPoints?: { element?: number; parent?: number }; 283 + pointerCaptureMode?: number; 284 + clipTo?: number; 270 285 zIndex?: number; 271 286 }; 272 287 } 288 + 289 + export const ATTACH_POINT = { 290 + LEFT_TOP: 0, 291 + LEFT_CENTER: 1, 292 + LEFT_BOTTOM: 2, 293 + CENTER_TOP: 3, 294 + CENTER_CENTER: 4, 295 + CENTER_BOTTOM: 5, 296 + RIGHT_TOP: 6, 297 + RIGHT_CENTER: 7, 298 + RIGHT_BOTTOM: 8, 299 + } as const; 300 + 301 + export const ATTACH_TO = { 302 + NONE: 0, 303 + PARENT: 1, 304 + ELEMENT_WITH_ID: 2, 305 + ROOT: 3, 306 + } as const; 307 + 308 + export const POINTER_CAPTURE_MODE = { 309 + CAPTURE: 0, 310 + PASSTHROUGH: 1, 311 + } as const; 312 + 313 + export const CLIP_TO = { 314 + NONE: 0, 315 + ATTACHED_PARENT: 1, 316 + } as const; 273 317 274 318 export interface Text { 275 319 directive: typeof OP_TEXT;
+28 -2
specs/renderer-spec.md
··· 609 609 - **`cornerRadius`** — per-corner radius values, producing rounded box-drawing 610 610 characters 611 611 - **`clip`** — clip region configuration for scroll containers 612 - - **`floating`** — floating-element configuration (offset, parent reference, 613 - attach points, z-index) 612 + - **`floating`** — floating-element configuration (offset, expansion, parent 613 + reference, attach target, structured attach points, pointer capture mode, clip 614 + target, z-index) 614 615 - **`scroll`** — scroll container configuration 616 + 617 + The current floating surface is: 618 + 619 + ```ts 620 + floating?: { 621 + x?: number; 622 + y?: number; 623 + expand?: { width?: number; height?: number }; 624 + parent?: number; 625 + attachTo?: number; 626 + attachPoints?: { 627 + element?: number; 628 + parent?: number; 629 + }; 630 + pointerCaptureMode?: number; 631 + clipTo?: number; 632 + zIndex?: number; 633 + } 634 + ``` 635 + 636 + This shape extends the earlier floating surface in two ways. First, 637 + `attachPoints` is structured as separate element and parent anchor values 638 + instead of a single packed enum. Second, the surface exposes additional Clay 639 + floating controls that were previously unavailable at the TypeScript layer: 640 + `expand`, `pointerCaptureMode`, and `clipTo`. 615 641 616 642 The `text()` constructor currently accepts: `color`, `fontSize`, 617 643 `letterSpacing`, `lineHeight`, and attribute flags (`bold`, `italic`,
+7 -1
src/clayterm.c
··· 546 546 if (mask & PROP_FLOATING) { 547 547 decl.floating.offset.x = rdf(buf, len, &i); 548 548 decl.floating.offset.y = rdf(buf, len, &i); 549 + decl.floating.expand.width = rdf(buf, len, &i); 550 + decl.floating.expand.height = rdf(buf, len, &i); 549 551 decl.floating.parentId = rd(buf, len, &i); 550 552 551 553 uint32_t fc = rd(buf, len, &i); 552 554 decl.floating.attachTo = fc & 0xff; 553 555 decl.floating.attachPoints.element = (fc >> 8) & 0xff; 554 556 decl.floating.attachPoints.parent = (fc >> 16) & 0xff; 555 - decl.floating.zIndex = (int16_t)((fc >> 24) & 0xff); 557 + decl.floating.pointerCaptureMode = (fc >> 24) & 0xff; 558 + 559 + uint32_t fd = rd(buf, len, &i); 560 + decl.floating.clipTo = fd & 0xff; 561 + decl.floating.zIndex = (int16_t)(fd >> 8); 556 562 } 557 563 558 564 Clay__ConfigureOpenElement(decl);
+54 -1
test/term.test.ts
··· 1 1 import { beforeEach, describe, expect, it } from "./suite.ts"; 2 2 import { createTerm, type Term } from "../term.ts"; 3 - import { close, fixed, grow, open, rgba, text } from "../ops.ts"; 3 + import { 4 + ATTACH_POINT, 5 + ATTACH_TO, 6 + close, 7 + fixed, 8 + grow, 9 + open, 10 + rgba, 11 + text, 12 + } from "../ops.ts"; 4 13 import { print } from "./print.ts"; 5 14 6 15 const decode = (bytes: Uint8Array) => new TextDecoder().decode(bytes); ··· 189 198 expect(result.info.get("nonexistent")).toBeUndefined(); 190 199 expect(result.info.get("")).toBeUndefined(); 191 200 }); 201 + }); 202 + 203 + it("renders a floating frame with structured attach points", () => { 204 + let out = print( 205 + decode( 206 + term.render([ 207 + open("root", { 208 + layout: { width: fixed(40), height: fixed(10), direction: "ttb" }, 209 + }), 210 + open("frame", { 211 + layout: { 212 + width: fixed(12), 213 + height: fixed(5), 214 + direction: "ttb", 215 + padding: { left: 1, top: 1 }, 216 + }, 217 + border: { 218 + color: rgba(255, 255, 255), 219 + left: 1, 220 + right: 1, 221 + top: 1, 222 + bottom: 1, 223 + }, 224 + floating: { 225 + x: 3, 226 + y: 1, 227 + attachTo: ATTACH_TO.ROOT, 228 + attachPoints: { 229 + element: ATTACH_POINT.CENTER_CENTER, 230 + parent: ATTACH_POINT.CENTER_CENTER, 231 + }, 232 + }, 233 + }), 234 + text("box"), 235 + close(), 236 + close(), 237 + ]).output, 238 + ), 239 + 40, 240 + 10, 241 + ); 242 + 243 + expect(out).toContain("│box │"); 244 + expect(out).toContain("┌──────────┐"); 192 245 }); 193 246 194 247 describe("row offset", () => {
+33
test/validate.test.ts
··· 78 78 it("rejects fractional color", () => { 79 79 expect(validate([text("hi", { color: 1.5 })])).toBe(false); 80 80 }); 81 + 82 + it("accepts structured floating attach points", () => { 83 + expect(validate([ 84 + open("x", { 85 + floating: { 86 + attachPoints: { element: 4, parent: 4 }, 87 + }, 88 + }), 89 + close(), 90 + ])).toBe(true); 91 + }); 92 + 93 + it("accepts floating expand and clipping fields", () => { 94 + expect(validate([ 95 + open("x", { 96 + floating: { 97 + expand: { width: 2, height: 3 }, 98 + pointerCaptureMode: 1, 99 + clipTo: 1, 100 + zIndex: 1024, 101 + }, 102 + }), 103 + close(), 104 + ])).toBe(true); 105 + }); 106 + 107 + it("rejects numeric floating attachPoints legacy shape", () => { 108 + expect(validate([ 109 + // deno-lint-ignore no-explicit-any 110 + open("x", { floating: { attachPoints: 4 as any } }), 111 + close(), 112 + ])).toBe(false); 113 + }); 81 114 }); 82 115 83 116 describe("validated", () => {
+10 -1
validate.ts
··· 83 83 const Floating = Type.Object({ 84 84 x: Type.Optional(Type.Number()), 85 85 y: Type.Optional(Type.Number()), 86 + expand: Type.Optional(Type.Object({ 87 + width: Type.Optional(Type.Number()), 88 + height: Type.Optional(Type.Number()), 89 + })), 86 90 parent: Type.Optional(Type.Integer({ minimum: 0 })), 87 91 attachTo: Type.Optional(u8), 88 - attachPoints: Type.Optional(u8), 92 + attachPoints: Type.Optional(Type.Object({ 93 + element: Type.Optional(u8), 94 + parent: Type.Optional(u8), 95 + })), 96 + pointerCaptureMode: Type.Optional(u8), 97 + clipTo: Type.Optional(u8), 89 98 zIndex: Type.Optional(u16), 90 99 }); 91 100