Skip to content

feat: Merge box-drawing lines into junctions where they meet - #94

Open
FXschwartz wants to merge 1 commit into
Norbert515:mainfrom
FXschwartz:5424-t-junction-blending
Open

feat: Merge box-drawing lines into junctions where they meet#94
FXschwartz wants to merge 1 commit into
Norbert515:mainfrom
FXschwartz:5424-t-junction-blending

Conversation

@FXschwartz

Copy link
Copy Markdown
Collaborator

Borders and dividers previously painted their cells independently, so a divider ending at a border stopped one cell short or overwrote it, the UI showed gaps where ├ ┤ ┬ ┴ ┼ junctions belong.

What this does

Adds arm-based box-character merging (the approach ratatui/tview use): every char in U+2500–257F maps to four arms with weights, drawing one line char onto another ORs the arms and emits the junction glyph.

  • mergeBoxCharacters() + isMergeableBoxCharacter() in lib/src/utils/box_line_merging.dart, exported from nocterm.dart
  • TerminalCanvas.drawText(..., blendBoxLines:), the internal mechanism the components paint with, defaults to false so ordinary text never merges (a inside a log line still overwrites its cell)
  • Divider/VerticalDivider: always merge; negative indent/endIndent reach into a border row/column and cap with half-arms ( + )
  • BoxBorder: corner cells always merge (overlaid panels tee into the border underneath), edges/titles/backgrounds occlude

Behavior change

Blending is always on for Divider, VerticalDivider, and BoxBorder, there is no per-component flag. Existing apps change appearance wherever lines overlap:

  • Dividers crossing borders/each other now form junctions instead of overwriting.
  • An overlaid panel's four corner cells merge with box characters beneath them. Content under the panel is still occluded by its edges, title, and background, but a corner landing exactly on a box char in the underlay (a inside a log line) renders as a junction.
  • Bordered boxes with a background color no longer paint that background over their border ring cells (the border pass paints the ring itself).

Added Example

dart example/box_line_blending_demo.dart - side-by-side indent: 0 vs indent: -1, crossings, mixed-weight tees, and an overlaid panel.

@FXschwartz FXschwartz self-assigned this Jul 10, 2026
Copilot AI review requested due to automatic review settings July 10, 2026 14:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves terminal UI rendering by introducing box-drawing “line blending” so overlapping borders/dividers merge into the correct Unicode junction glyphs (e.g. ├ ┤ ┬ ┴ ┼) instead of leaving gaps or overwriting each other.

Changes:

  • Added arm/weight-based merging for box-drawing characters via mergeBoxCharacters() / isMergeableBoxCharacter().
  • Extended TerminalCanvas.drawText() with blendBoxLines to optionally merge box characters while drawing.
  • Enabled blending behavior for Divider/VerticalDivider (including half-arm end caps for negative indents) and for BoxBorder corners; added tests + a demo example.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
test/utils/box_line_merging_test.dart Unit tests for low-level box-character merging behavior (tees/crosses/weights/mergeability).
test/rendering/border_blend_test.dart Rendering tests validating overlay border corners tee into underlying borders and background doesn’t erase underlying border ring cells.
test/components/divider_blend_test.dart Rendering tests ensuring dividers tee into borders with negative indents and form crosses when intersecting.
lib/src/utils/box_line_merging.dart Implements the arm/weight model and merge logic for Unicode box-drawing characters.
lib/src/framework/terminal_canvas.dart Adds blendBoxLines option to drawText() to merge box-drawing characters with existing buffer content.
lib/src/components/divider.dart Makes dividers blend by default (except ascii), adds half-arm caps for negative indents, and updates component docs.
lib/src/components/decorated_box.dart Adjusts background fill to avoid erasing border ring cells; enables blending for border corner cells.
lib/nocterm.dart Exports the new box line merging utilities.
example/box_line_blending_demo.dart Adds an example showcasing divider/border blending and negative-indent behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

charToUse = chars.horizontal;
}
_setCell(canvas, left, top, charToUse, borderStyle);
_setCell(canvas, left, top, charToUse, borderStyle, blend: true);
final leftTopChar =
!border.left.isNone ? chars.topLeft : chars.horizontal;
_setCell(canvas, left, top, leftTopChar, borderStyle);
_setCell(canvas, left, top, leftTopChar, borderStyle, blend: true);
final rightTopChar =
!border.right.isNone ? chars.topRight : chars.horizontal;
_setCell(canvas, right, top, rightTopChar, borderStyle);
_setCell(canvas, right, top, rightTopChar, borderStyle, blend: true);
charToUse = chars.horizontal;
}
_setCell(canvas, left, bottom, charToUse, style);
_setCell(canvas, left, bottom, charToUse, style, blend: true);
final leftBottomChar =
!border.left.isNone ? chars.bottomLeft : chars.horizontal;
_setCell(canvas, left, bottom, leftBottomChar, style);
_setCell(canvas, left, bottom, leftBottomChar, style, blend: true);
final rightBottomChar =
!border.right.isNone ? chars.bottomRight : chars.horizontal;
_setCell(canvas, right, bottom, rightBottomChar, style);
_setCell(canvas, right, bottom, rightBottomChar, style, blend: true);
Comment on lines +16 to +20
/// junctions instead of overwriting them. With a negative [indent] or
/// [endIndent] it reaches outside its own bounds; those cells paint half-arm
/// characters (`╶`/`╴`), so an end landing on a box border becomes a tee
/// (`├`/`┤`). An end landing on a cell that is not a box-drawing character
/// keeps the half-arm stub, so only reach into cells known to hold borders.
Comment on lines +72 to +76
/// junctions instead of overwriting them. With a negative [indent] or
/// [endIndent] it reaches outside its own bounds; those cells paint half-arm
/// characters (`╷`/`╵`), so an end landing on a box border becomes a tee
/// (`┬`/`┴`). An end landing on a cell that is not a box-drawing character
/// keeps the half-arm stub, so only reach into cells known to hold borders.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use T-junction box-drawing characters to remove gaps between borders and dividers in the serverpod start TUI

2 participants