summaryrefslogtreecommitdiff
path: root/src/web/app.mjs
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 16:28:57 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:24:42 -0300
commit1ab4c071be58033d5452f69797ca8e2c32240cff (patch)
treeafa108a1c372276da1d2c62b6c05d8f167cc81b8 /src/web/app.mjs
parentc6ab9db0fdbcb8d16f666749194bb111218b96a8 (diff)
downloadpardes-1ab4c071be58033d5452f69797ca8e2c32240cff.tar.gz
pardes-1ab4c071be58033d5452f69797ca8e2c32240cff.zip
Separate tag text geometry from physical pane grips
Diffstat (limited to 'src/web/app.mjs')
-rw-r--r--src/web/app.mjs59
1 files changed, 59 insertions, 0 deletions
diff --git a/src/web/app.mjs b/src/web/app.mjs
index 312627df..2a4f8a65 100644
--- a/src/web/app.mjs
+++ b/src/web/app.mjs
@@ -54,6 +54,16 @@ export class DomRenderer {
const cursorY = this.wasm.pardes_cursor_y();
const cursorBar = this.wasm.pardes_cursor_bar() !== 0;
const raw = new Array(count);
+ const tagCovered = new Uint8Array(count);
+ if (this.wasm.pardes_tag_layer_limit) {
+ for (let i = 0; i < this.wasm.pardes_tag_layer_limit(); i++) {
+ const val = field => this.wasm.pardes_tag_layer_value(i, field);
+ if (!val(0)) continue;
+ for (let x = val(4); x < Math.min(cols, val(4) + val(6)); x++) {
+ if (val(5) < rows) tagCovered[val(5) * cols + x] = 1;
+ }
+ }
+ }
for (let index = 0; index < count; index += 1) {
const off = index * CELL_SIZE;
@@ -66,6 +76,7 @@ export class DomRenderer {
const y = Math.floor(index / cols);
const cursor = x === cursorX && y === cursorY;
const cell = this.cells[index];
+ cell.style.visibility = tagCovered[index] ? "hidden" : "";
if (cell.textContent !== text) cell.textContent = text;
const styleKey = `${fg}:${bg}:${attrs}:${cursor ? 1 : 0}:${cursorBar ? 1 : 0}`;
if (cell.pardesStyle !== styleKey) {
@@ -75,10 +86,58 @@ export class DomRenderer {
raw[index] = { text, fg, bg, attrs };
}
this.renderBodyLayers();
+ this.renderTagLayers();
this.snapshot = { cols, rows, cells: raw, cursorX, cursorY, cursorBar };
return this.snapshot;
}
+ renderTagLayers() {
+ if (!this.wasm.pardes_tag_layer_limit || !this.cellWidth) return;
+ if (!this.tagRoot?.isConnected) {
+ this.tagRoot = this.root.ownerDocument.createElement("div");
+ this.tagRoot.style.cssText = "position:absolute;inset:0;pointer-events:none;overflow:hidden";
+ this.root.style.position = "relative";
+ this.root.append(this.tagRoot);
+ }
+ this.tagNodes ??= new Map();
+ const nodes = [];
+ const bw = this.cellWidth, bh = this.cellHeight, tw = this.taglineWidth, th = this.taglineHeight;
+ for (let i = 0; i < this.wasm.pardes_tag_layer_limit(); i++) {
+ const values = Array.from({length:11}, (_,field) => this.wasm.pardes_tag_layer_value(i,field));
+ const cols = values[0];
+ if (!cols) continue;
+ const ptr = this.wasm.pardes_tag_layer_cells(i);
+ if (!ptr) continue;
+ const bytes = new Uint8Array(this.wasm.memory.buffer,ptr,cols*CELL_SIZE);
+ const offset = this.wasm.pardes_tagline_band_offset(values[5],this.rowCount*bh*256,Math.round(bh*256),Math.round(th*256))/256;
+ const geometry = [bw,bh,tw,th,offset,...values].join(":");
+ const cached = this.tagNodes.get(i);
+ if (cached && cached.geometry === geometry && cached.bytes.length === bytes.length && cached.bytes.every((byte,index)=>byte===bytes[index])) {
+ nodes.push(cached.node);
+ continue;
+ }
+ const view = new DataView(this.wasm.memory.buffer,ptr,cols*CELL_SIZE);
+ const layer = this.root.ownerDocument.createElement("div");
+ layer.dataset.tagLayer = String(i);
+ layer.style.cssText = `position:absolute;overflow:hidden;white-space:pre;left:${values[4]*bw}px;top:${values[5]*bh+offset}px;width:${values[6]*bw}px;height:${th}px;background:${color(values[10],DEFAULT_BG)}`;
+ for (let col = 0; col < cols; col++) {
+ const off = col*CELL_SIZE;
+ const cell = this.root.ownerDocument.createElement("span");
+ cell.className = "pardes-cell";
+ cell.textContent = decoder.decode(bytes.subarray(off,off+view.getUint8(off+18))) || " ";
+ applyStyle(cell,view.getUint32(off+8,true),view.getUint32(off+12,true),view.getUint16(off+16,true),col===values[8],values[9]!==0);
+ cell.style.textAlign = "left";
+ cell.style.width = `${tw}px`;
+ cell.style.height = `${th}px`;
+ cell.style.lineHeight = `${th}px`;
+ layer.append(cell);
+ }
+ this.tagNodes.set(i,{geometry,bytes:bytes.slice(),node:layer});
+ nodes.push(layer);
+ }
+ if (nodes.length !== this.tagRoot.childNodes.length || nodes.some((node,index)=>node!==this.tagRoot.childNodes[index])) this.tagRoot.replaceChildren(...nodes);
+ }
+
renderBodyLayers() {
if (!this.wasm.pardes_body_layer_limit || !this.cellWidth) return;
if (!this.layerRoot?.isConnected) {