{"version":3,"file":"overlayText.mjs","names":[],"sources":["../../../../src/tui/workflow/overlayText.ts"],"sourcesContent":["/**\n * Text primitives shared by every doom overlay in this extension.\n *\n * Kept in one place because each of them encodes a decision that has to hold on\n * every surface: how a cell is clipped, which end of a path survives, and what\n * counts as a keystroke that carries text.\n */\n\nimport { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nexport const ELLIPSIS = '…';\nexport const CURSOR_BLOCK = '█';\nexport const SELECTION_MARKER = '›';\n/** pi-tui brackets a truncation ellipsis with this; see `fit`. */\nconst HARD_RESET = '\\x1b[0m';\nconst ESCAPE = '\\x1b';\nconst FIRST_PRINTABLE_CODE_POINT = 0x20;\nconst DELETE_CODE_POINT = 0x7f;\n\n/**\n * Truncates and pads to an exact column count.\n *\n * `truncateToWidth` wraps its ellipsis in a hard `\\x1b[0m`, which would strip a\n * row's background for everything after a clipped cell. Nothing here sets a\n * colour that a full reset is the correct end for -- the theme closes its own\n * with `\\x1b[39m` and `\\x1b[49m` -- so the injected resets are dropped.\n */\nexport function fit(text: string, width: number): string {\n  const clipped = truncateToWidth(text, Math.max(0, width), ELLIPSIS).replaceAll(HARD_RESET, '');\n  return clipped + ' '.repeat(Math.max(0, width - visibleWidth(clipped)));\n}\n\n/**\n * Fits a line that carries its own colour, keeping every escape it contains.\n *\n * `fit` above drops hard resets, which is right for a themed row whose\n * background must survive a clipped cell and wrong for captured terminal\n * output: those resets are what END the child's colour runs, and deleting them\n * bleeds a colour through the rest of the panel. This closes the line with a\n * reset instead, before the padding, so no attribute escapes the row.\n */\nexport function fitTerminalLine(text: string, width: number): string {\n  const safeWidth = Math.max(0, width);\n  const clipped = truncateToWidth(text, safeWidth, ELLIPSIS);\n  const padding = ' '.repeat(Math.max(0, safeWidth - visibleWidth(clipped)));\n  return clipped.includes(ESCAPE) ? `${clipped}${HARD_RESET}${padding}` : `${clipped}${padding}`;\n}\n\n/** Right-aligns a trailing cluster against a left one, both clipped to fit. */\nexport function rightAligned(left: string, right: string, width: number): string {\n  const rightWidth = visibleWidth(right);\n  const leftWidth = Math.max(0, width - rightWidth - 1);\n  return fit(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);\n}\n\n/**\n * Trims from the front rather than the tail.\n *\n * For a path the file name identifies the thing, while the\n * `automations/marketing-workflows/` prefix every entry shares does not.\n */\nexport function pathTail(text: string, width: number): string {\n  if (width <= 0) return '';\n  if (visibleWidth(text) <= width) return text;\n  return `${ELLIPSIS}${text.slice(text.length - Math.max(0, width - 1))}`;\n}\n\n/**\n * Keystrokes that carry no text: escape sequences, control keys, delete.\n *\n * Indexed rather than iterated so surrogate pairs stay intact; both halves sit\n * far above the control range, so no emoji is ever mistaken for one.\n */\nexport function isControlInput(data: string): boolean {\n  for (let index = 0; index < data.length; index++) {\n    const code = data.charCodeAt(index);\n    if (code < FIRST_PRINTABLE_CODE_POINT || code === DELETE_CODE_POINT) return true;\n  }\n  return false;\n}\n\n/**\n * Whitespace-separated terms, each of which must appear somewhere in the row's\n * searchable text. Splitting on whitespace is what lets `short video` match a\n * row whose words are divided between its title and its file name.\n */\nexport function matchesQuery(haystack: string, query: string): boolean {\n  const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n  if (terms.length === 0) return true;\n  const lowered = haystack.toLowerCase();\n  return terms.every((term) => lowered.includes(term));\n}\n"],"mappings":";;AAcA,MAAM,aAAa;AACnB,MAAM,SAAS;AACf,MAAM,6BAA6B;AACnC,MAAM,oBAAoB;;;;;;;;;AAU1B,SAAgB,IAAI,MAAc,OAAuB;CACvD,MAAM,UAAU,gBAAgB,MAAM,KAAK,IAAI,GAAG,KAAK,GAAA,GAAW,CAAC,CAAC,WAAW,YAAY,EAAE;CAC7F,OAAO,UAAU,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,aAAa,OAAO,CAAC,CAAC;AACxE;;;;;;;;;;AAWA,SAAgB,gBAAgB,MAAc,OAAuB;CACnE,MAAM,YAAY,KAAK,IAAI,GAAG,KAAK;CACnC,MAAM,UAAU,gBAAgB,MAAM,WAAA,GAAmB;CACzD,MAAM,UAAU,IAAI,OAAO,KAAK,IAAI,GAAG,YAAY,aAAa,OAAO,CAAC,CAAC;CACzE,OAAO,QAAQ,SAAS,MAAM,IAAI,GAAG,UAAU,aAAa,YAAY,GAAG,UAAU;AACvF;;AAGA,SAAgB,aAAa,MAAc,OAAe,OAAuB;CAC/E,MAAM,aAAa,aAAa,KAAK;CACrC,MAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,aAAa,CAAC;CACpD,OAAO,IAAI,MAAM,SAAS,IAAI,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,YAAY,UAAU,CAAC,IAAI,IAAI,OAAO,UAAU;AAC/G;;;;;;;AAQA,SAAgB,SAAS,MAAc,OAAuB;CAC5D,IAAI,SAAS,GAAG,OAAO;CACvB,IAAI,aAAa,IAAI,KAAK,OAAO,OAAO;CACxC,OAAO,IAAc,KAAK,MAAM,KAAK,SAAS,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC;AACtE;;;;;;;AAQA,SAAgB,eAAe,MAAuB;CACpD,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;EAChD,MAAM,OAAO,KAAK,WAAW,KAAK;EAClC,IAAI,OAAO,8BAA8B,SAAS,mBAAmB,OAAO;CAC9E;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,aAAa,UAAkB,OAAwB;CACrE,MAAM,QAAQ,MAAM,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CAC7D,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,MAAM,UAAU,SAAS,YAAY;CACrC,OAAO,MAAM,OAAO,SAAS,QAAQ,SAAS,IAAI,CAAC;AACrD"}