How 'Fill' Width Behavior Works in Horizontal Containers in CLI‑Anything

TL;DR: In CLI‑Anything, a width: 'fill' declaration inside a horizontal container resolves to a fixed 300 px fallback rather than stretching to fill available space, because the intrinsicSize helper lacks parent width context during horizontal stack measurement.

The CLI‑Anything layout engine, located in sketch/agent-harness/src/layout.js, handles dimension calculations for terminal UI components. Understanding how the 'fill' width token behaves in horizontal containers is critical for building responsive CLI interfaces that don't break unexpectedly.

The Core 'Fill' Width Logic

The 'fill' width behavior is implemented in the intrinsicSize helper function. When processing a layer's dimensions, the engine checks for the 'fill' string in two locations:

// layout.js – intrinsic size handling
// https://github.com/HKUDS/CLI-Anything/blob/main/sketch/agent-harness/src/layout.js#L52-L57
if (w === 'fill') w = parentWidth || 300;
if (!w && layer._resolvedStyle?.width === 'fill') w = parentWidth || 300;

This logic reveals two critical characteristics:

  • 'fill' requires parentWidth to resolve dynamically
  • Without parent context, it falls back to a hardcoded 300 pixels

Why Horizontal Stacks Break 'Fill' Expectations

The horizontal container implementation in layoutHorizontalStack does not propagate parent width to child measurements:

// layout.js – horizontal-stack layout
// https://github.com/HKUDS/CLI-Anything/blob/main/sketch/agent-harness/src/layout.js#L33-L41
function layoutHorizontalStack(layers, config, containerWidth, containerHeight) {
  const padH = config.paddingHorizontal || 0;
  const padLeft = config.paddingLeft || padH;
  // ...
  const totalChildWidth = sizes.reduce((s, sz) => s + sz.width, 0);
  // ...
}

When measuring children, the engine calls intrinsicSize(l, undefined) — passing undefined as the parent width. This triggers the 300 px fallback for any 'fill' declaration.

The Layout Calculation Flow

  1. Measure phase: Each child calls intrinsicSize without parent width context
  2. 'fill' resolution: Converts to 300 px instead of remaining space
  3. Position phase: Children are laid out left-to-right using resolved widths
  4. Container sizing: Total width equals sum of child widths plus gaps and padding

Practical Code Example

// Example: horizontal container with mixed width declarations
const layout = {
  type: 'horizontal-stack',
  paddingHorizontal: 10,
  gap: 5,
};

const layers = [
  { type: 'rectangle', width: 120, height: 40 },   // fixed 120 px
  { type: 'rectangle', width: 'fill', height: 40 } // resolves to 300 px
];

const result = computeLayout(layers, layout, 0, 0);
console.log(result);

Output structure:

index x y width height
0 10 0 120 40
1 135 0 300 40

The second rectangle receives 300 px despite the 'fill' declaration, because layoutHorizontalStack provides no parentWidth during measurement.

Working Around the Limitation

Since CLI‑Anything's horizontal containers don't support true fill-to-available-space behavior, consider these alternatives:

  • Pre-calculate widths: Compute remaining space manually before passing to the layout engine
  • Use vertical containers: The 'fill' height behavior may resolve more predictably in vertical-stack layouts
  • Fixed layouts: Specify explicit pixel widths for all horizontal children
  • Nested groups: Wrap children in groups that provide explicit parentWidth context through _resolvedStyle

Key Source Files

File Purpose
sketch/agent-harness/src/layout.js Core layout engine with intrinsicSize and layoutHorizontalStack
sketch/agent-harness/src/builder.js Layer object construction and _resolvedStyle handling
sketch/agent-harness/src/primitives.js Primitive layer type definitions

Summary

  • 'fill' width requires parentWidth to resolve dynamically in CLI‑Anything
  • Horizontal containers pass undefined as parent width during child measurement
  • 300 px fallback is applied universally when parent context is missing
  • Container width equals sum of resolved child widths, not a constraint that children fill

Frequently Asked Questions

Why doesn't 'fill' width stretch to fill remaining space in horizontal stacks?

The layoutHorizontalStack function calls intrinsicSize(l, undefined) for each child, providing no parent width context. Without this context, the intrinsicSize helper cannot calculate proportional fill and defaults to 300 px. This is an implementation limitation in the current layout engine.

Can I force a child to fill remaining horizontal space?

Not natively. You must pre-calculate the available width (container width minus fixed children and gaps) and assign that value explicitly. The layout engine does not support flex-style growth factors or剩余空间分配 in horizontal containers.

Does 'fill' height behave the same way in vertical containers?

The vertical stack implementation (layoutVerticalStack) likely faces similar constraints, though the specific resolution path depends on whether containerHeight is propagated during measurement. Check the intrinsicSize calls in your specific version of layout.js to confirm.

Where is the 300 px fallback value defined?

The fallback appears twice in intrinsicSize at lines 52–57 of sketch/agent-harness/src/layout.js: once for direct layer.width checks and once for layer._resolvedStyle.width checks. Both use parentWidth || 300 as the resolution formula.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →