{"version":3,"names":["Attr","createSelection","anchor","focus","clearSelection","state","selectionRange","a","b","row","col","startRow","startCol","endRow","endCol","applySelectionOverlay","buf","range","rows","colStart","Math","max","colEnd","min","cols","get","char","cell","getMutable","attrs","Inverse","getSelectedText","lines","line","width","push","trimEnd","length","pop","join","copyToClipboard","text","write","encoded","Buffer","from","toString"],"sources":["selection.ts"],"sourcesContent":["/**\n * Text selection: track anchor/focus, overlay selected cells, extract text, copy.\n *\n * Selection operates in screen-space on the FrameBuffer — it doesn't know\n * about nodes or scroll offsets. The overlay is applied as a post-paint\n * pass before diffing, using Attr.Inverse XOR to highlight.\n */\nimport { Attr, type FrameBuffer } from \"./frame-buffer.ts\";\n\nexport interface SelectionPos {\n\tcol: number;\n\trow: number;\n}\n\nexport interface SelectionState {\n\tanchor: SelectionPos | null;\n\tfocus: SelectionPos | null;\n}\n\nexport function createSelection(): SelectionState {\n\treturn { anchor: null, focus: null };\n}\n\nexport function clearSelection(state: SelectionState): void {\n\tstate.anchor = null;\n\tstate.focus = null;\n}\n\n/** Normalize anchor/focus into reading-order start/end. */\nexport function selectionRange(state: SelectionState): {\n\tendCol: number;\n\tendRow: number;\n\tstartCol: number;\n\tstartRow: number;\n} | null {\n\tif (!state.anchor || !state.focus) return null;\n\n\tconst a = state.anchor;\n\tconst b = state.focus;\n\n\t// Reading order: top-to-bottom, left-to-right\n\tif (a.row < b.row || (a.row === b.row && a.col <= b.col)) {\n\t\treturn { startRow: a.row, startCol: a.col, endRow: b.row, endCol: b.col };\n\t}\n\treturn { startRow: b.row, startCol: b.col, endRow: a.row, endCol: a.col };\n}\n\n/** Toggle Attr.Inverse on selected cells in the buffer. */\nexport function applySelectionOverlay(\n\tbuf: FrameBuffer,\n\tstate: SelectionState,\n): void {\n\tconst range = selectionRange(state);\n\tif (!range) return;\n\n\tconst { startRow, startCol, endRow, endCol } = range;\n\n\tfor (let row = startRow; row <= endRow && row < buf.rows; row++) {\n\t\tlet colStart = row === startRow ? Math.max(0, startCol) : 0;\n\t\tlet colEnd = row === endRow ? Math.min(buf.cols - 1, endCol) : buf.cols - 1;\n\n\t\t// Clip leading empty cells on all rows\n\t\twhile (colStart < colEnd && buf.get(colStart, row).char === \" \") {\n\t\t\tcolStart++;\n\t\t}\n\t\t// Clip trailing empty cells on non-end rows (end row follows cursor)\n\t\tif (row !== endRow) {\n\t\t\twhile (colEnd > colStart && buf.get(colEnd, row).char === \" \") {\n\t\t\t\tcolEnd--;\n\t\t\t}\n\t\t}\n\t\t// Entirely blank row — skip it\n\t\tif (colStart === colEnd && buf.get(colStart, row).char === \" \") continue;\n\n\t\tfor (let col = colStart; col <= colEnd; col++) {\n\t\t\t// getMutable materializes blank slots so highlighted gaps invert too\n\t\t\t// (and never mutates the shared frozen blank).\n\t\t\tconst cell = buf.getMutable(col, row);\n\t\t\t// XOR inverse for visual highlight\n\t\t\tcell.attrs ^= Attr.Inverse;\n\t\t}\n\t}\n}\n\n/** Extract text from selected cells, joining rows with newlines. */\nexport function getSelectedText(\n\tbuf: FrameBuffer,\n\tstate: SelectionState,\n): string {\n\tconst range = selectionRange(state);\n\tif (!range) return \"\";\n\n\tconst { startRow, startCol, endRow, endCol } = range;\n\tconst lines: string[] = [];\n\n\tfor (let row = startRow; row <= endRow && row < buf.rows; row++) {\n\t\tconst colStart = row === startRow ? Math.max(0, startCol) : 0;\n\t\tconst colEnd =\n\t\t\trow === endRow ? Math.min(buf.cols - 1, endCol) : buf.cols - 1;\n\n\t\tlet line = \"\";\n\t\tfor (let col = colStart; col <= colEnd; col++) {\n\t\t\tconst cell = buf.get(col, row);\n\t\t\tif (cell.width === 0) continue; // skip continuation cells\n\t\t\tline += cell.char;\n\t\t}\n\t\tlines.push(line.trimEnd());\n\t}\n\n\t// Trim trailing empty lines\n\twhile (lines.length > 0 && lines[lines.length - 1] === \"\") {\n\t\tlines.pop();\n\t}\n\n\treturn lines.join(\"\\n\");\n}\n\n/** Copy text to system clipboard via OSC 52. */\nexport function copyToClipboard(\n\ttext: string,\n\twrite: (s: string) => void,\n): void {\n\tif (!text) return;\n\tconst encoded = Buffer.from(text, \"utf-8\").toString(\"base64\");\n\t// OSC 52: set clipboard 'c' (system clipboard), BEL-terminated for compatibility\n\twrite(`\\x1b]52;c;${encoded}\\x07`);\n}\n"],"mappings":"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","ignoreList":[]}