head-tail-cap.ts 1.6 KB

123456789101112131415161718192021222324252627282930313233
  1. // Head/tail height-cap arithmetic shared by the block primitives (TerminalBlock,
  2. // SearchBlock) and matching the TUI transcript's collapsed tool card, so a long
  3. // result's head and tail slices agree across every surface. The split is
  4. // `ceil(maxLines / 2)` head rows and the remainder as tail rows; a result within
  5. // the cap shows every row and hides none.
  6. /** The head/tail split metrics for a capped list. */
  7. export interface HeadTailCap {
  8. /** Rows beyond the cap (list length − maxLines); ≤ 0 means nothing is hidden. */
  9. hidden: number
  10. /** Whether the list is over the cap and not expanded, so it shows a head/tail slice. */
  11. capped: boolean
  12. /** Head-slice row count: `ceil(maxLines / 2)`. */
  13. headLines: number
  14. /** Tail-slice row count: the remainder after the head. */
  15. tailLines: number
  16. }
  17. /**
  18. * Compute the head/tail cap metrics for a list of `total` rows against `maxLines`,
  19. * given whether the surface is expanded. Pure arithmetic; the caller slices its
  20. * own rows with `headLines`/`tailLines` so a block can layer its own concerns
  21. * (SearchBlock restores a tail file header) on top.
  22. * @param total - the list's row count.
  23. * @param maxLines - the collapsed-height cap in rows.
  24. * @param expanded - whether the surface is expanded (uncaps the list).
  25. * @returns the split metrics.
  26. */
  27. export function headTailCap(total: number, maxLines: number, expanded: boolean): HeadTailCap {
  28. const hidden = total - maxLines
  29. const headLines = Math.ceil(maxLines / 2)
  30. return { hidden, capped: hidden > 0 && !expanded, headLines, tailLines: maxLines - headLines }
  31. }