Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,7 @@ pub fn build(b: *std.Build) void {
native_api_tests.addFileArg(b.path("packages/core/test/surface_recipes.test.ts"));
native_api_tests.addFileArg(b.path("packages/core/test/widget_metrics.test.ts"));
native_api_tests.addFileArg(b.path("packages/core/test/layout_coordination.test.ts"));
native_api_tests.addFileArg(b.path("packages/core/test/render_coordination.test.ts"));
native_api_tests.addFileArg(b.path("packages/core/test/component_composition.test.ts"));
native_api_tests.addFileArg(b.path("packages/core/test/view_frontend.test.ts"));
native_api_tests.has_side_effects = true;
Expand Down Expand Up @@ -5422,6 +5423,7 @@ fn externalCoreFixtureModule(
stage_run.addFileInput(b.path("packages/core/src/measurement_coordination.ts"));
stage_run.addFileInput(b.path("packages/core/src/flow_measurement.ts"));
stage_run.addFileInput(b.path("packages/core/src/layout_coordination.ts"));
stage_run.addFileInput(b.path("packages/core/src/render_coordination.ts"));
stage_run.addFileInput(b.path("packages/core/src/stream_policy.ts"));
stage_run.addArg("--src");
stage_run.addDirectoryArg(spec.src_dir);
Expand Down
1 change: 1 addition & 0 deletions build/app.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,7 @@ fn tsCoreStage(
stage_run.addFileInput(dep.path("packages/core/src/measurement_coordination.ts"));
stage_run.addFileInput(dep.path("packages/core/src/flow_measurement.ts"));
stage_run.addFileInput(dep.path("packages/core/src/layout_coordination.ts"));
stage_run.addFileInput(dep.path("packages/core/src/render_coordination.ts"));
stage_run.addFileInput(dep.path("packages/core/src/stream_policy.ts"));
if (typescript_view) {
stage_run.addArg("--view-markup");
Expand Down
143 changes: 143 additions & 0 deletions packages/core/src/render_coordination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/** Ordered paint capabilities. The host owns widget/text/path buffers and
* executes these copied recipes; the compiler arena never owns a display list. */
function nscvRenderRecipe(request: Uint8Array): Uint8Array {
if (request.length !== 32 || request[0] !== 43 || request[1] !== 1 || request[2]! > 1 || request[3]! > 2)
throw new Error("invalid render recipe header");
const w = new DataView(request.buffer, request.byteOffset, request.byteLength);
const flags = w.getUint32(4, true), kind = w.getUint32(20, true), retained = request[2] === 1;
if (flags > 2047 || kind > 62 || w.getUint32(24, true) !== 0 || w.getUint32(28, true) !== 0 ||
retained && (w.getUint32(8, true) !== 0 || w.getUint32(12, true) !== 0))
throw new Error("invalid render recipe facts");
const result = new Uint8Array(64), out = new DataView(result.buffer);
out.setUint32(0, 1, true);
// Direct-tree depth errors precede hiding, and never initialize a command.
if (!retained && (w.getUint32(12, true) !== 0 || w.getUint32(8, true) >= 32)) {
out.setUint32(4, 2, true); return result;
}
if (!retained && (flags & 256) !== 0) { out.setUint32(4, 1, true); return result; }
let count = 0;
const emit = (op: number, arg: number = 0): void => {
if (count >= 6) throw new Error("render recipe capacity exceeded");
out.setUint32(16 + count * 8, op, true); out.setUint32(20 + count * 8, arg, true); count++;
};
// op: backdrop, draw, scrim, children, separators, scrollbars, reactions.
// Child arguments: low byte clip (none/content/frame/disclosure), bit 8
// bubble palette, bit 9 direct-tree segment stamps. Palette restoration is
// implicit on return; reactions always use the caller's palette.
const contentClip = (flags & 1) !== 0 ? 1 : 0;
const virtual = (flags & 2) !== 0, spans = (flags & 4) !== 0;
emit(0);
let draw = -1;
switch (kind) {
case 0: case 1: case 2: draw = 0; break;
case 43: draw = 1; break;
case 12: draw = 2; break;
case 17: draw = 3; break;
case 18: draw = 4; break;
case 19: draw = 5; break;
case 20: draw = 6; break;
case 21: draw = 7; break;
case 14: draw = 8; break;
case 15: draw = 9; break;
case 16: case 22: draw = 10; break;
case 23: draw = 11; break;
case 24: case 25: draw = 12; break;
case 26: draw = (flags & 8) !== 0 ? 14 : spans ? 15 : 13; break;
case 27: draw = 16; break;
case 28: draw = 17; break;
case 61: draw = 18; break;
case 62: draw = 19; break;
case 29: draw = 20; break;
case 30: draw = 21; break;
case 31: case 32: case 50: draw = 22; break;
case 33: draw = 23; break;
case 34: draw = 24; break;
case 35: case 36: draw = 25; break;
case 39: draw = (flags & 16) !== 0 ? 26 : 25; break;
case 37: case 38: draw = 27; break;
case 40: draw = 28; break;
case 41: draw = 29; break;
case 42: draw = 30; break;
case 44: draw = 31; break;
case 45: draw = 32; break;
case 46: draw = 33; break;
case 47: draw = 34; break;
case 48: draw = 35; break;
case 49: draw = 36; break;
case 51: draw = 37; break;
case 52: draw = 38; break;
case 53: draw = 39; break;
case 58: draw = 40; break;
case 54: draw = 41; break;
case 55: draw = 42; break;
case 56: draw = 43; break;
case 60: draw = 44; break;
}
if (kind >= 19 && kind <= 21 && (flags & 1024) !== 0) emit(2);
if (draw >= 0) emit(1, draw);
if (kind === 15) { emit(3, 256 | contentClip); emit(6); }
else if (kind === 14) {
const disclosure = retained ? request[3]! : (flags & 64) !== 0 ? 2 : 0;
if (disclosure !== 0) emit(3, disclosure === 1 ? 3 : contentClip);
} else if (kind === 6) {
emit(3, 2); if ((flags & 32) === 0) emit(5, 1);
} else if (kind === 4 || kind === 5) {
const scroll = retained && virtual;
emit(3, scroll ? contentClip === 1 ? 1 : 2 : contentClip);
if (scroll && (flags & 32) === 0) emit(5, 0);
emit(4, scroll ? 1 : 0);
} else if (retained && (kind === 3 || kind === 7) && virtual) {
emit(3, contentClip === 1 ? 1 : 2); if ((flags & 32) === 0) emit(5, 0);
} else if (retained) emit(3, contentClip);
else if (kind === 9) {
const stamps = (flags & 512) !== 0 || w.getFloat32(16, true) <= 0;
emit(3, contentClip | (stamps ? 512 : 0));
} else if (kind <= 13 || kind === 16 || kind === 17 || kind === 18 ||
kind >= 19 && kind <= 25 || kind === 42 || kind === 43 || kind === 57 || kind === 59 || kind === 60 ||
kind === 44 && !spans && (flags & 128) !== 0) emit(3, contentClip);
out.setUint32(8, count, true); return result;
}

/** Direct-tree siblings include hidden nodes: visiting a hidden child still
* checks its depth. Layer ties retain source order; segment positions count
* visible source siblings, independently of the order in which they paint. */
function nscvRenderChildren(request: Uint8Array): Uint8Array {
if (request.length < 32 || request[0] !== 44 || request[1] !== 1 || request[2]! > 1 || request[3] !== 0)
throw new Error("invalid render child header");
const w = new DataView(request.buffer, request.byteOffset, request.byteLength), count = w.getUint32(4, true);
if (request.length !== 32 + count * 12 || w.getUint32(24, true) !== 0 || w.getUint32(28, true) !== 0)
throw new Error("invalid render child shape");
let visible = 0;
for (let i = 0; i < count; i++) {
const at = 32 + i * 12, flags = w.getUint32(at + 4, true);
if (w.getUint32(at, true) > 62 || flags > 3) throw new Error("invalid render child facts");
if ((flags & 1) === 0) visible++;
if ((flags & 2) === 0 && w.getUint32(at + 8, true) !== 0) throw new Error("invalid absent render layer");
}
const layer = (i: number): number => {
const at = 32 + i * 12, kind = w.getUint32(at, true);
if ((w.getUint32(at + 4, true) & 2) !== 0) return w.getInt32(at + 8, true);
return w.getInt32(kind >= 19 && kind <= 21 ? 12 : kind === 23 || kind === 24 || kind === 25 ? 16 : kind === 40 ? 20 : 8, true);
};
const result = new Uint8Array(8 + count * 8), out = new DataView(result.buffer); out.setUint32(0, 1, true); out.setUint32(4, count, true);
let previous = -1, previousLayer = 0;
for (let dest = 0; dest < count; dest++) {
let best = -1, bestLayer = 0;
for (let i = 0; i < count; i++) {
const value = layer(i);
if (previous >= 0 && (value < previousLayer || value === previousLayer && i <= previous)) continue;
if (best < 0 || value < bestLayer || value === bestLayer && i < best) { best = i; bestLayer = value; }
}
if (best < 0) throw new Error("render child ordering made no progress");
let segment = 0;
if (request[2] === 1 && visible > 1) {
let ordinal = 0;
if ((w.getUint32(36 + best * 12, true) & 1) === 0)
for (let i = 0; i < best; i++) if ((w.getUint32(36 + i * 12, true) & 1) === 0) ordinal++;
segment = ordinal === 0 ? 1 : ordinal === visible - 1 ? 3 : 2;
}
out.setUint32(8 + dest * 8, best, true); out.setUint32(12 + dest * 8, segment, true);
previous = best; previousLayer = bestLayer;
}
return result;
}
2 changes: 2 additions & 0 deletions packages/core/src/runtime_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ export function native_window_policy(request: Uint8Array): Uint8Array {
if (request[0] === 40) return nscvLayoutAdmission(request);
if (request[0] === 41) return nscvLayoutChildren(request);
if (request[0] === 42) return nscvLayoutRetained(request);
if (request[0] === 43) return nscvRenderRecipe(request);
if (request[0] === 44) return nscvRenderChildren(request);
const data = new DataView(request.buffer, request.byteOffset, request.byteLength);
let at = 0;
const byte = (): number => {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/view_frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,4 +930,5 @@ const viewPrelude = "\n// Portable Native components compiled beside the committ
readFileSync(new URL("./measurement_coordination.ts", import.meta.url), "utf8") +
readFileSync(new URL("./flow_measurement.ts", import.meta.url), "utf8") +
readFileSync(new URL("./layout_coordination.ts", import.meta.url), "utf8") +
readFileSync(new URL("./render_coordination.ts", import.meta.url), "utf8") +
readFileSync(new URL("./stream_policy.ts", import.meta.url), "utf8");
72 changes: 72 additions & 0 deletions packages/core/test/render_coordination.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import assert from "node:assert/strict";
import test from "node:test";
import { readFileSync } from "node:fs";
import { stripTypeScriptTypes } from "node:module";
const source = readFileSync(new URL("../src/render_coordination.ts", import.meta.url), "utf8");
const { nscvRenderRecipe: recipe, nscvRenderChildren: children } = await import(`data:text/javascript;base64,${Buffer.from(stripTypeScriptTypes(source) + "\nexport {nscvRenderRecipe,nscvRenderChildren};").toString("base64")}`);
const wire = (b: Uint8Array) => new DataView(b.buffer, b.byteOffset, b.byteLength);
function packet(kind: number, retained = false, flags = 0, disclosure = 0): Uint8Array {
const b = new Uint8Array(32); b.set([43,1,retained ? 1 : 0,disclosure]);
wire(b).setUint32(4,flags,true);wire(b).setUint32(20,kind,true);return b;
}
function commands(b: Uint8Array): number[][] {
const r = recipe(b), w = wire(r);
assert.equal(r.length,64);assert.equal(w.getUint32(0,true),1);assert.ok(r.subarray(16+w.getUint32(8,true)*8).every(v=>v===0));
return Array.from({length:w.getUint32(8,true)},(_,i)=>[w.getUint32(16+i*8,true),w.getUint32(20+i*8,true)]);
}
test("bubble recipes preserve the palette cascade and page-token reactions after children",()=>{
for(const retained of [false,true])assert.deepEqual(commands(packet(15,retained,1)),[[0,0],[1,9],[3,257],[6,0]]);
});
test("table and virtual scrolling order clips children bars and separators correctly",()=>{
assert.deepEqual(commands(packet(5,false,2)),[[0,0],[3,0],[4,0]]);
assert.deepEqual(commands(packet(5,true,2)),[[0,0],[3,2],[5,0],[4,1]]);
assert.deepEqual(commands(packet(5,true,3|32)),[[0,0],[3,1],[4,1]]);
assert.deepEqual(commands(packet(6,true,32)),[[0,0],[3,2]]);
assert.deepEqual(commands(packet(6,false)),[[0,0],[3,2],[5,1]]);
});
test("disclosure recipes preserve discrete tree and three-state retained emission",()=>{
assert.deepEqual(commands(packet(14,false,0,1)),[[0,0],[1,8]]);
assert.deepEqual(commands(packet(14,false,64|1)),[[0,0],[1,8],[3,1]]);
for(const [pose,clip]of [[0,-1],[1,3],[2,1]])assert.deepEqual(commands(packet(14,true,1,pose!)),[[0,0],[1,8],...(clip!<0?[]:[[3,clip!]])]);
});
test("retained leaves and direct cells retain their different child contracts",()=>{
assert.deepEqual(commands(packet(31,false)),[[0,0],[1,22]]);
assert.deepEqual(commands(packet(31,true)),[[0,0],[1,22],[3,0]]);
assert.deepEqual(commands(packet(44,false,128)),[[0,0],[1,31],[3,0]]);
assert.deepEqual(commands(packet(44,false,128|4)),[[0,0],[1,31]]);
assert.deepEqual(commands(packet(26,false,8|4)),[[0,0],[1,14]]);
assert.deepEqual(commands(packet(39,true,16)),[[0,0],[1,26],[3,0]]);
});
test("modal scrims precede chrome and segment stamps honor style and exceptional gaps",()=>{
assert.deepEqual(commands(packet(19,true,1024|1)),[[0,0],[2,0],[1,5],[3,1]]);
for(const [gap,detached,stamp]of [[0,false,true],[2,false,false],[-1,false,true],[NaN,false,false],[Infinity,true,true]] as const){
const b=packet(9,false,detached?512:0);wire(b).setFloat32(16,gap,true);
assert.deepEqual(commands(b),[[0,0],[3,stamp?512:0]]);
}
});
test("depth errors precede hidden suppression and retain complete empty command tails",()=>{
const b=packet(31,false,256);assert.equal(wire(recipe(b)).getUint32(4,true),1);
for(const depth of [32n,9007199254740993n,0xffffffffffffffffn]){
wire(b).setBigUint64(8,depth,true);const r=recipe(b);assert.equal(wire(r).getUint32(4,true),2);assert.ok(r.subarray(8).every(v=>v===0));
}
});
function childPacket(rows: [number,boolean,number|null][], stamp=true):Uint8Array{
const b=new Uint8Array(32+rows.length*12),w=wire(b);b.set([44,1,stamp?1:0,0]);w.setUint32(4,rows.length,true);
for(const [i,value]of [7,20,-5,30].entries())w.setInt32(8+i*4,value,true);
rows.forEach(([kind,hidden,layer],i)=>{const at=32+i*12;w.setUint32(at,kind,true);w.setUint32(at+4,(hidden?1:0)|(layer===null?0:2),true);if(layer!==null)w.setInt32(at+8,layer,true);});return b;
}
function entries(b:Uint8Array):number[][]{const r=children(b),w=wire(r);return Array.from({length:w.getUint32(4,true)},(_,i)=>[w.getUint32(8+i*8,true),w.getUint32(12+i*8,true)]);}
test("direct paint ordering retains hidden visits, exact signed layers and visible source segments",()=>{
const b=childPacket([[31,false,2147483647],[23,false,null],[31,true,-2147483648],[31,false,null],[40,false,null],[19,false,null],[31,false,null]]);
assert.deepEqual(entries(b),[[2,1],[1,2],[3,2],[6,3],[5,2],[4,2],[0,1]]);
b[2]=0;assert.deepEqual(entries(b).map(e=>e[1]),[0,0,0,0,0,0,0]);
assert.deepEqual(entries(childPacket([])),[]);assert.deepEqual(entries(childPacket([[31,true,null],[31,true,null]])),[[0,0],[1,0]]);
});
test("all kind and flag recipes are bounded, owned and reject malformed packets",()=>{
for(let kind=0;kind<63;kind++)for(const retained of [false,true])for(let flags=0;flags<2048;flags++){
const b=packet(kind,retained,flags,flags%3),saved=b.slice(),r=recipe(b),copy=r.slice();assert.deepEqual(b,saved);b.fill(255);assert.deepEqual(r,copy);assert.ok(wire(r).getUint32(8,true)<=6);
}
for(const at of [0,1,2,3,5,12,20,24,31]){const b=packet(31,true);b[at]=255;assert.throws(()=>recipe(b),/invalid/);}
for(const at of [0,1,2,3,4,24,32,36,40]){const b=childPacket([[31,false,null]]);b[at]=255;assert.throws(()=>children(b),/invalid/);}
const b=childPacket([[31,false,null],[31,false,null]]),saved=b.slice(),r=children(b),copy=r.slice();assert.deepEqual(b,saved);b.fill(255);assert.deepEqual(r,copy);
});
2 changes: 1 addition & 1 deletion packages/core/test/view_frontend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ test("the view frontend and compiled portable component bundle typecheck", () =>
};
// Production joins these policies in one module before compiling the view.
const bundlePath = new URL("../src/view_components_typecheck.ts", import.meta.url).pathname;
const bundle = ["view_components.ts", "runtime_policy.ts", "control_appearance.ts", "component_construction.ts", "widget_motion.ts", "component_composition.ts", "code_content.ts", "chart_content.ts", "markdown_content.ts", "widget_audits.ts", "widget_routing.ts", "widget_changes.ts", "widget_paint.ts", "widget_presentation.ts", "widget_paint_walk.ts", "control_geometry.ts", "control_content.ts", "surface_recipes.ts", "widget_metrics.ts", "intrinsic_measure.ts", "measurement_coordination.ts", "flow_measurement.ts", "layout_coordination.ts", "stream_policy.ts"]
const bundle = ["view_components.ts", "runtime_policy.ts", "control_appearance.ts", "component_construction.ts", "widget_motion.ts", "component_composition.ts", "code_content.ts", "chart_content.ts", "markdown_content.ts", "widget_audits.ts", "widget_routing.ts", "widget_changes.ts", "widget_paint.ts", "widget_presentation.ts", "widget_paint_walk.ts", "control_geometry.ts", "control_content.ts", "surface_recipes.ts", "widget_metrics.ts", "intrinsic_measure.ts", "measurement_coordination.ts", "flow_measurement.ts", "layout_coordination.ts", "render_coordination.ts", "stream_policy.ts"]
.map(name => readFileSync(new URL(`../src/${name}`, import.meta.url), "utf8")).join("\n");
const host = ts.createCompilerHost(options);
const readSource = host.getSourceFile.bind(host);
Expand Down
Loading
Loading