Skip to content

Commit b43ceaf

Browse files
A box round a picture spans the width in a terminal again (#53)
2.14.2 (#51) fitted a box holding only a picture to the picture. In a scene log that box stood narrower than every other pose's box and rule. It is drawn across the width again, with its padding, and the picture at its left. HTML keeps fitting it (ms-box-picture). This reverts the Frame and Figure parts of 62345a3. Claude-Session: https://claude.ai/code/session_018ztDNVgzxM8V5WKWBRs2EW Co-authored-by: Claude <noreply@anthropic.com>
1 parent ba0ce27 commit b43ceaf

4 files changed

Lines changed: 16 additions & 69 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5252

5353
### Fixed
5454

55+
- **A box round a picture spans the width in a terminal again.** 2.14.2 fitted a box holding only a
56+
picture to the picture, so in a scene log it stood narrower than every box and rule around it. It is
57+
drawn across the width again, with its padding, and the picture at its left. HTML still fits it
58+
(`ms-box-picture`).
5559
- **An MXP client draws a figure's picture in its cells.** A figure's `<IMAGE>` carried no size, so a
5660
client drew the picture at its own size under the line it was named on, outside any box round it. It
5761
is now sized in the cells laid out for it (`W=12c H=5c`), the art's or `PictureCells`', and a client
@@ -73,11 +77,6 @@ follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7377
right but none above. The picture now aligns to the top of its line, and a box whose body is only a
7478
picture on a line of its own (`ms-box-picture`) has no padding and is no wider than the picture.
7579
A `Frame`'s `Padding` reaches the HTML as `--ms-pad` (it was always `1ch`).
76-
- **A box round a picture fits it in a terminal too.** A terminal that draws pictures got the box across
77-
the whole width with the picture in its left corner, so it stood wider than the rules and lines
78-
around it. A `Frame` whose body is only a picture on a line of its own, for a reader shown that
79-
picture, now has no padding and is as wide as the picture (or its title, if that is wider). A reader
80-
shown the art or the description instead still gets the box across the width.
8180

8281
## 2.11.2 — 2026-10-07
8382

‎MarkupString.Tests/Ansi/TerminalFeatureTests.cs‎

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -192,41 +192,19 @@ public async Task KittyKeepsABoxAroundThePictureAligned()
192192
}
193193

194194
/// <summary>
195-
/// A box round nothing but a picture fits it, as it does in HTML: a terminal drew the box across the whole
196-
/// width with the picture in its left corner, wider than the rules and lines around it.
195+
/// A box round nothing but a picture spans the width, as a box round text does, so it lines up with the boxes
196+
/// and rules around it; the picture sits at its left.
197197
/// </summary>
198198
[Test]
199-
public async Task ABoxRoundOnlyAPictureFitsIt()
199+
public async Task ABoxRoundOnlyAPictureSpansTheWidth()
200200
{
201201
var options = new AnsiOutputOptions(Features: TerminalFeatures.KittyGraphics) { Pictures = new Source(RedBlue()) };
202202
var laid = Laid(new Frame(new Figure(Cat, MarkupText.Empty)), 40, new PictureCells(6, 2));
203203

204204
var lines = RenderString(laid, options).Split('\n');
205205

206-
await Assert.That(lines.Select(VisibleCells).Distinct().ToArray()).IsEquivalentTo(new[] { 8 });
207-
await Assert.That(laid.ToPlainText().Split('\n')[0]).IsEqualTo("┌──────┐");
208-
}
209-
210-
/// <summary>A titled box round a picture is still as wide as its title needs.</summary>
211-
[Test]
212-
public async Task ABoxRoundAPictureKeepsRoomForItsTitle()
213-
{
214-
var laid = Laid(new Frame(new Figure(Cat, MarkupText.Empty)) { Title = MarkupText.Plain("A long title") }, 40, new PictureCells(6, 2));
215-
216-
var lines = laid.ToPlainText().Split('\n');
217-
218-
await Assert.That(lines.Select(line => line.Length).Distinct().ToArray()).IsEquivalentTo(new[] { 19 });
219-
await Assert.That(lines[0]).IsEqualTo("┌─┤ A long title ├┐");
220-
}
221-
222-
/// <summary>A reader shown the picture's text instead gets the box across the width, as with any other text.</summary>
223-
[Test]
224-
public async Task ABoxRoundAPictureNotShownKeepsItsWidth()
225-
{
226-
var laid = BlockLayout.Build(new Frame(new Figure(Cat, MarkupText.Empty)), 40,
227-
context: new LayoutContext { Pictures = (_, _) => null });
228-
229-
await Assert.That(laid.ToPlainText().Split('\n').Select(line => line.Length).Distinct().ToArray()).IsEquivalentTo(new[] { 40 });
206+
await Assert.That(lines.Select(VisibleCells).Distinct().ToArray()).IsEquivalentTo(new[] { 40 });
207+
await Assert.That(laid.ToPlainText().Split('\n')[0]).IsEqualTo("┌" + new string('─', 38) + "┐");
230208
}
231209

232210
[Test]

‎MarkupString/Layout/Blocks/Figure.cs‎

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ public sealed record Figure(ImageMarkup Image, MarkupText Art) : Block
2222
/// <inheritdoc/>
2323
public override void Draw(LayoutContext context, int width, IList<MarkupText> lines)
2424
{
25-
var art = ArtLines();
25+
var art = Art.Length == 0
26+
? null
27+
: Art.Split("\n").Select(line => line.Text.EndsWith('\r') ? line.Substring(0, line.Length - 1) : line).ToArray();
28+
29+
// Art of line breaks alone has no cells to hold a picture, so it is no art.
30+
if (art is not null && art.Max(line => line.DisplayWidth) == 0) art = null;
2631

2732
// The picture is on its rows whoever reads them: a terminal that has its pixels draws it in their cells,
2833
// MXP or Pueblo writes it once, and everything else reads what the rows hold.
@@ -90,27 +95,6 @@ public override void Draw(LayoutContext context, int width, IList<MarkupText> li
9095
}
9196
}
9297

93-
/// <summary>The art's lines, or none: art of line breaks alone has no cells to hold a picture, so it is no art.</summary>
94-
private MarkupText[]? ArtLines()
95-
{
96-
if (Art.Length == 0) return null;
97-
var art = Art.Split("\n").Select(line => line.Text.EndsWith('\r') ? line.Substring(0, line.Length - 1) : line).ToArray();
98-
return art.Max(line => line.DisplayWidth) == 0 ? null : art;
99-
}
100-
101-
/// <summary>
102-
/// The cells wide the picture is drawn in at <paramref name="width"/> for a reader <paramref name="context"/>
103-
/// draws it for, or none when the reader gets text instead.
104-
/// </summary>
105-
internal int? PictureWidth(LayoutContext context, int width)
106-
{
107-
if (Image.Source.Length == 0 || context.Pictures is not { } pictures) return null;
108-
var artWidth = ArtLines()?.Max(line => line.DisplayWidth);
109-
return pictures(Image, Math.Min(width, artWidth ?? width)) is { Columns: > 0, Rows: > 0 } cells
110-
? Math.Min(width, artWidth ?? cells.Columns)
111-
: null;
112-
}
113-
11498
/// <summary>
11599
/// The rows of cells the picture is drawn in, each marked with the picture and its <see cref="PictureRow"/>
116100
/// over what a client the picture does not reach shows there: the art, which keeps its own size, or, with no

‎MarkupString/Layout/Blocks/Frame.cs‎

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,8 @@ public override void Draw(LayoutContext context, int width, IList<MarkupText> li
4949
{
5050
var style = context.Paint(context.Border(Border));
5151
var none = style.Name == BorderStyle.None.Name;
52-
var sides = style.Left.DisplayWidth + style.Right.DisplayWidth;
5352
var padding = BlockText.Blank(Padding);
54-
var inner = Math.Max(1, width - sides - padding.DisplayWidth * 2);
55-
56-
// A box round nothing but a picture the reader is shown fits it, as it does in HTML: no padding, and no
57-
// wider than the picture, or than the title needs.
58-
if (Body is Figure { Float: FigureFloat.None, Beside: null } figure
59-
&& figure.PictureWidth(context, Math.Max(1, width - sides)) is { } picture)
60-
{
61-
var title = Title is { Length: > 0 } t
62-
? t.DisplayWidth + style.TitleOpen.DisplayWidth + style.TitleClose.DisplayWidth + (TitleAlignment is Alignment.Left or Alignment.Right ? 2 : 1)
63-
: 0;
64-
padding = MarkupText.Empty;
65-
inner = Math.Max(1, Math.Min(width - sides, Math.Max(picture, title)));
66-
width = inner + sides;
67-
}
53+
var inner = Math.Max(1, width - style.Left.DisplayWidth - style.Right.DisplayWidth - padding.DisplayWidth * 2);
6854

6955
if (!none || Title is { Length: > 0 })
7056
lines.Add(BlockText.Edge(style.TopLeft, style.Top, style.TopRight, Title is null ? null : context.Paint(theme => theme.TitleColor, Title), TitleAlignment, style, width));

0 commit comments

Comments
 (0)