diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-15 16:50:50 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-15 17:24:42 -0300 |
| commit | 9ce11bfeff73b96704e521b41cffe4625b02f103 (patch) | |
| tree | 3aedcf15b6adc9c1ef4d1acff2146b7c352e6ba6 /src/web | |
| parent | 96725011b43d7bf56ac02e710ea4ef4a76f05351 (diff) | |
| download | pardes-9ce11bfeff73b96704e521b41cffe4625b02f103.tar.gz pardes-9ce11bfeff73b96704e521b41cffe4625b02f103.zip | |
Reorder columns using aligned and distinctly colored tag grips
Diffstat (limited to 'src/web')
| -rw-r--r-- | src/web/app.mjs | 37 |
1 files changed, 33 insertions, 4 deletions
diff --git a/src/web/app.mjs b/src/web/app.mjs index 2a4f8a65..3dcf6ab5 100644 --- a/src/web/app.mjs +++ b/src/web/app.mjs @@ -55,10 +55,16 @@ export class DomRenderer { const cursorBar = this.wasm.pardes_cursor_bar() !== 0; const raw = new Array(count); const tagCovered = new Uint8Array(count); + const tagGrips = new Map(); 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; + const gripCols = this.wasm.pardes_grip_columns?.() ?? 2; + if (val(1) !== 0 && val(4) >= gripCols && this.cellHeight) { + const offset = this.wasm.pardes_tagline_band_offset(val(5),rows*this.cellHeight*256,Math.round(this.cellHeight*256),Math.round(this.taglineHeight*256))/256; + for (let x = val(4)-gripCols; x < val(4); x++) tagGrips.set(val(5)*cols+x,offset); + } for (let x = val(4); x < Math.min(cols, val(4) + val(6)); x++) { if (val(5) < rows) tagCovered[val(5) * cols + x] = 1; } @@ -77,6 +83,10 @@ export class DomRenderer { const cursor = x === cursorX && y === cursorY; const cell = this.cells[index]; cell.style.visibility = tagCovered[index] ? "hidden" : ""; + const gripOffset = tagGrips.get(index); + cell.style.height = gripOffset === undefined ? "" : `${this.taglineHeight}px`; + cell.style.lineHeight = gripOffset === undefined ? "" : `${this.taglineHeight}px`; + cell.style.marginTop = gripOffset === undefined ? "" : `${gripOffset}px`; if (cell.textContent !== text) cell.textContent = text; const styleKey = `${fg}:${bg}:${attrs}:${cursor ? 1 : 0}:${cursorBar ? 1 : 0}`; if (cell.pardesStyle !== styleKey) { @@ -86,12 +96,12 @@ export class DomRenderer { raw[index] = { text, fg, bg, attrs }; } this.renderBodyLayers(); - this.renderTagLayers(); + this.renderTagLayers(raw); this.snapshot = { cols, rows, cells: raw, cursorX, cursorY, cursorBar }; return this.snapshot; } - renderTagLayers() { + renderTagLayers(canonical) { if (!this.wasm.pardes_tag_layer_limit || !this.cellWidth) return; if (!this.tagRoot?.isConnected) { this.tagRoot = this.root.ownerDocument.createElement("div"); @@ -110,10 +120,15 @@ export class DomRenderer { 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 gripCols = this.wasm.pardes_grip_columns?.() ?? 2; + const gripX = values[4] - gripCols; + const gripCell = canonical[values[5]*this.cols+gripX]; + const grip = values[1] !== 0 && gripX >= 0 && gripCell?.text === " " && th >= 6 && tw >= 3; + const geometry = [bw,bh,tw,th,offset,...values,grip,gripCell?.fg].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); + if (cached.grip) nodes.push(cached.grip); continue; } const view = new DataView(this.wasm.memory.buffer,ptr,cols*CELL_SIZE); @@ -132,8 +147,22 @@ export class DomRenderer { cell.style.lineHeight = `${th}px`; layer.append(cell); } - this.tagNodes.set(i,{geometry,bytes:bytes.slice(),node:layer}); + let gripNode = null; + if (grip) { + gripNode = this.root.ownerDocument.createElement("div"); + gripNode.dataset.tagGrip = String(i); + const centerX = (gripX+gripCols/2)*bw, centerY = values[5]*bh+offset+th/2; + const half = Math.max(1,Math.min(3,bw-2)); + gripNode.style.cssText = "position:absolute;inset:0;pointer-events:none"; + for (const dy of [-2,1]) { + const mark = this.root.ownerDocument.createElement("div"); + mark.style.cssText = `position:absolute;left:${Math.floor(centerX-half)}px;top:${Math.floor(centerY+dy)}px;width:${2*half}px;height:1px;background:${color(gripCell.fg,DEFAULT_FG)}`; + gripNode.append(mark); + } + } + this.tagNodes.set(i,{geometry,bytes:bytes.slice(),node:layer,grip:gripNode}); nodes.push(layer); + if (gripNode) nodes.push(gripNode); } if (nodes.length !== this.tagRoot.childNodes.length || nodes.some((node,index)=>node!==this.tagRoot.childNodes[index])) this.tagRoot.replaceChildren(...nodes); } |
