Skip to content

Commit 6c7cd70

Browse files
committed
Narrow the side gutter shared by every shell surface
The gutter held at two columns per side from 60 columns wide all the way up, never scaling down or up with the terminal, which read as excess air on a wide pane. Drop it to one column, still enough to keep content off the frame edge at every width the gutter is affordable.
1 parent e0b3724 commit 6c7cd70

5 files changed

Lines changed: 35 additions & 23 deletions

File tree

‎docs/TUI.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ goal/task/agents strips, then progress, then the prompt itself shrinks one
3636
row at a time down to its 3-row base — never the transcript
3737
(`COLLAPSE_ORDER` in `zones.ts`).
3838

39+
Horizontally, every surface sits inside one shared gutter
40+
(`resolveSideMargin`, `src/tui-opentui/geometry/margins.ts`) so the shell reads
41+
as a single column of content rather than stacked panes. The gutter is one
42+
column per side at every width that can afford it, and zero below
43+
`MARGIN_MIN_COLUMNS` (40), where every column belongs to content. There is no
44+
middle tier: one column is already enough to keep content off the frame edge,
45+
which is the gutter's entire job, and anything wider only read as excess air on
46+
a wide pane. The gutter costs no rows.
47+
3948
The prompt box's border carries the metadata that would otherwise cost a
4049
titlebar row: the model label sits right-aligned in the top rule; the brand
4150
lockup sits at the left of the bottom rule with the working directory and git

‎src/tui-opentui/geometry/index.ts‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ export {
2121
export {
2222
BOTTOM_MARGIN_MIN_ROWS,
2323
BOTTOM_MARGIN_ROWS,
24-
MARGIN_FULL_MIN_COLUMNS,
2524
MARGIN_MIN_COLUMNS,
26-
NARROW_SIDE_MARGIN,
2725
SIDE_MARGIN,
2826
TOP_PAD_MIN_TRANSCRIPT_ROWS,
2927
TOP_PAD_ROWS,

‎src/tui-opentui/geometry/margins.ts‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,24 @@
99
* here can take a row away from it.
1010
*/
1111

12-
/** Gutter columns on each side once the terminal can afford them. */
13-
export const SIDE_MARGIN = 2
14-
15-
/** Half gutter for terminals too narrow to spend four columns on air. */
16-
export const NARROW_SIDE_MARGIN = 1
17-
18-
/** At or above this width the full gutter is affordable. */
19-
export const MARGIN_FULL_MIN_COLUMNS = 60
12+
/**
13+
* Gutter columns on each side once the terminal can afford them.
14+
*
15+
* One column at every width the gutter exists at all. A single column is
16+
* already enough to keep content off the frame edge, which is the whole job,
17+
* and a wider gutter only read as excess air on a wide pane. There is no
18+
* middle tier: a width that can spare a column gets one, and a width that
19+
* cannot gets none.
20+
*/
21+
export const SIDE_MARGIN = 1
2022

2123
/** Below this width every column belongs to content: the gutter goes to zero. */
2224
export const MARGIN_MIN_COLUMNS = 40
2325

2426
/** Gutter width for a terminal of `columns` columns. */
2527
export function resolveSideMargin(columns: number): number {
2628
const cols = Math.max(0, Math.floor(columns))
27-
if (cols >= MARGIN_FULL_MIN_COLUMNS) return SIDE_MARGIN
28-
if (cols >= MARGIN_MIN_COLUMNS) return NARROW_SIDE_MARGIN
29-
return 0
29+
return cols >= MARGIN_MIN_COLUMNS ? SIDE_MARGIN : 0
3030
}
3131

3232
/** Columns left for content after both gutters. */

‎src/tui-opentui/landing.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ describe("landing screen", () => {
305305
expect(ruleRow).toBe(SIZE.height - 1)
306306
const row = painted[ruleRow]!
307307
// Left end of the rule, inside the shell gutter, costing no row.
308-
expect(row.startsWith(" ╰─ ")).toBe(true)
308+
expect(row.startsWith(" ╰─ ")).toBe(true)
309309
expect(row.trimEnd().endsWith("╯")).toBe(true)
310310
} finally {
311311
shell.dispose()

‎src/tui-opentui/margins.test.ts‎

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
import { describe, expect, test } from "bun:test"
77
import {
88
BOTTOM_MARGIN_MIN_ROWS,
9-
MARGIN_FULL_MIN_COLUMNS,
109
MARGIN_MIN_COLUMNS,
11-
NARROW_SIDE_MARGIN,
1210
SIDE_MARGIN,
1311
resolveBottomMarginRows,
1412
resolveContentWidth,
@@ -29,15 +27,22 @@ function frameRows(h: Harness): readonly string[] {
2927
}
3028

3129
describe("side margin resolution", () => {
32-
test("steps down with width and floors at zero", () => {
33-
expect(resolveSideMargin(120)).toBe(SIDE_MARGIN)
34-
expect(resolveSideMargin(MARGIN_FULL_MIN_COLUMNS)).toBe(SIDE_MARGIN)
35-
expect(resolveSideMargin(MARGIN_FULL_MIN_COLUMNS - 1)).toBe(
36-
NARROW_SIDE_MARGIN,
37-
)
38-
expect(resolveSideMargin(MARGIN_MIN_COLUMNS)).toBe(NARROW_SIDE_MARGIN)
30+
test("one column at every affordable width, and zero below the floor", () => {
31+
expect(SIDE_MARGIN).toBe(1)
32+
for (const columns of [MARGIN_MIN_COLUMNS, 60, 80, 120, 200]) {
33+
expect(resolveSideMargin(columns)).toBe(1)
34+
}
3935
expect(resolveSideMargin(MARGIN_MIN_COLUMNS - 1)).toBe(0)
4036
expect(resolveSideMargin(10)).toBe(0)
37+
expect(resolveSideMargin(0)).toBe(0)
38+
})
39+
40+
test("content never touches the first or last column of the frame", () => {
41+
// The gutter's whole job. A one-column gutter is the narrowest thing that
42+
// can do it, so this is what would break if it were ever spent.
43+
for (const columns of [80, 120, 200]) {
44+
expect(resolveContentWidth(columns)).toBe(columns - 2)
45+
}
4146
})
4247

4348
test("content width never collapses below one column", () => {

0 commit comments

Comments
 (0)