Skip to content

harness: composer text field horizontally scrolls instead of wrapping and growing #702

Description

@btipling

Summary

The composer text field horizontally scrolls when a line is longer than the field. It should wrap and grow the input vertically (up to the existing chrome cap), then scroll inside the field vertically — never a horizontal gutter.

This is already the locked contract (docs/harness-limits.md Wrap / grow; plans #457 / #579; repo no-h-scroll policy #344 / #457 / #579). The flags are set (multiline + break_lines) but a long unwrapped line still pan the caret sideways instead of wrapping.

Observed

  1. Focus the canvas composer (Message the model…).
  2. Type (or paste) a long single line with no newlines — longer than the field width.

Actual: the line stays one row; the field scrolls horizontally with the caret. The chrome stays idle height (~44 px). Hard to read; violates the no-h-scroll policy.

Expected: the line wraps at the field width. The composer chrome grows up with wrapped lines (idle ~44 px → cap 124 px). Past the cap, the entry scrolls vertically inside. No horizontal gutter past the trailing ▶/■ icons.

Typing Enter (newline) already grows the field. The miss is soft wrap of a long line, not explicit newlines.

Likely seam

Composer textEntry in native/harness/src/ui.zig:

var te = dvui.textEntry(@src(), .{
    .text = .{ .buffer = state.prompt_buf[0..] },
    .placeholder = "Message the model…",
    .multiline = true,
    .break_lines = true,
}, .{
    .expand = .horizontal,
    .gravity_y = 0.5,
    .min_size_content = .{ .w = 120, .h = metrics.TOUCH_H - 2 * metrics.COMPOSER_TE_PAD },
    .max_size_content = .{ .w = dvui.max_float_safe, .h = metrics.COMPOSER_INPUT_MAX_H - 2 * metrics.COMPOSER_TE_PAD },
    // …
});
  • break_lines = true is already on.
  • max_size_content.w = dvui.max_float_safe was set so a 0 width + 2×COMPOSER_TE_PAD bake would not cap the layout at 10 px. A wrap-on-width TextEntry needs a finite content width (the allocated row minus the icon column). max_float_safe lets the inner layout believe it can grow infinitely wide, so wrap never fires and the widget h-scrolls.
  • Dynamic hug (composer_last_h) samples wrapped height after te.deinit(). If wrap never happens, hug stays idle.

Queue-row editor (native/harness/src/ui/queue_band.zig) is single-line (multiline = false) by design — out of scope unless the same no-h-scroll policy should apply there too (ellipsis, not grow).

Goal

  • Long lines wrap at the composer field width.
  • Chrome grows vertically with wrapped content up to COMPOSER_MAX_CHROME_H (124 px), then vertical internal scroll (COMPOSER_INPUT_MAX_H = 120 px).
  • No horizontal scroll / gutter on the composer (no-h-scroll policy).
  • Idle single short line still hugs ~44 px. Enter-to-newline + Ctrl/Cmd+Enter send unchanged.

Non-goals

  • Changing COMPOSER_INPUT_MAX_H / COMPOSER_MAX_CHROME_H.
  • Dual DOM chat input.
  • Making the queue-row editor multi-line (unless a follow-up).
  • Transcript wrap (already a different path).

Related

Suggested next: type a long URL / sentence with no newline; the caret should wrap onto a second line and the chrome should grow, not slide sideways.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingharnessHarness / agent UIui

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions