Skip to content

Initial render: input sits one character into the placeholder until the window is resized #116

Open

Description

@AThraen

Confirmed present in v0.7.0-rc. Accepted for this release — cosmetic, and it self-corrects.

Symptom

On restore, the input line in a Claude pane is drawn about one character into the placeholder text, so the leading character of the placeholder survives (e.g. Thi there typed into Try "how do I log an error?"). Text elsewhere can also wrap oddly mid-line.

Resizing the window, or changing layout, fixes every pane immediately.

What we know

That resize detail is the clue. A resize fires the page's ResizeObserver → fitAddon.fit() → a new column count → term.onResize → the PTY is resized. So the correction is a column-count change, which means at startup xterm and the PTY disagree about the width. Claude wraps its placeholder at one column while xterm draws at another, and the offset follows.

What it is NOT — two theories already disproved

Recording these so nobody re-treads them:

  1. Font loading. fix(terminal): re-fit once the font has loaded, not on a timing guess #113 added a re-fit on document.fonts.ready, on the theory that xterm measured the fallback font's advance width before Cascadia Code loaded. It was a no-op. document.fonts.ready resolves once at page load and stays resolved, so the callback ran immediately with the same metrics.
  2. document.fonts.load(). Also a no-op here: it only matches CSS-connected FontFace objects, i.e. @font-face rules. There are none — the fonts in play are OS-installed — so it resolves having matched nothing.

The setTimeout(50) / setTimeout(250) fits in terminal-init.js are what currently correct the separate 0×0-container case, and they do not fix this.

Suggested next step: measure, don't theorise

Two guesses have already been wrong. The decisive probe is small — log both sides and compare:

  • In terminal-init.js, on every fit() and every term.onResize, log the resulting cols/rows.
  • In TerminalBridge, log the cols/rows the PTY is actually set to, with the session name.

If they diverge, the log shows exactly when and which side is stale. If they agree, the cause is elsewhere and both remaining theories die.

One specific thing worth checking in that data: term.onResize only fires when dimensions change. If the PTY is started from a stale TerminalSize and a later fit() happens to compute the same cols xterm already had, no resize message is sent and the PTY keeps the wrong width indefinitely — which would match "only an external resize fixes it".

Related

Activity

  1. AThraen commented on Sep 6, 2026

    @AThraen
    ContributorAuthor

    Update: the attempted fix from #113 is gone from the code too

    #115 replaced the document.fonts.ready re-fit with a requestAnimationFrame one (Assets/terminal-init.js:114-124, comment preserved in place). So theory 1 in this issue is now disproved and removed — nobody reading the source will find a fonts-based fit and assume the case is handled.

    The current fit sites, for whoever picks this up:

    Site Trigger
    terminal-init.js:33 initial, at construction
    :79 ResizeObserver
    :94-95 host-posted focus / fit messages
    :110-124 post-setOptions, then again on the next requestAnimationFrame
    :270 page-side mousedown (the capture-phase activate handler)
    :276-277 setTimeout 50ms / 250ms — these are what correct the separate 0×0-container case
    :294-296 still a document.fonts.ready fit; harmless, and per the above it resolves immediately

    The suggested next step in the issue body is unchanged and is still the right one: log cols/rows on every fit() and every term.onResize, and log what TerminalBridge actually sets the PTY to, with the session name. Two theories have now been wrong; the third guess should be a measurement.

    The specific hypothesis worth testing first remains the one about onResize firing only on change: if the PTY is started from a stale TerminalSize and a later fit() computes the same cols xterm already had, no resize message is sent and the PTY keeps the wrong width indefinitely — which is exactly the shape of "only an external resize fixes it".

    Still present, still cosmetic, still self-correcting on any resize.

  2. AThraen commented on Sep 9, 2026

    @AThraen
    ContributorAuthor

    This was generated by AI during triage.

    Related: #134 — a separate, coarser startup render bug (Claude panes garbled, rows colliding/duplicated, corrected by a repaint). Filed separately per this issue's own warning not to conflate the one-char offset with the similar-symptom 0×0 case.

    The two do share one mechanism, and it's the trap named in this issue: term.onResize fires only when dimensions change, so a late fit() that recomputes the same wrong cols posts nothing and the PTY keeps its stale width. #134 argues that's the root pathology rather than a detail, and that the logging proposed here (both sides' cols/rows) should land first since it would confirm both issues at once.

    #134 also found that FitTerminal() is only called on attach in the Single layout branch — every multi-pane branch attaches wrappers with no fit — which would explain why this reproduces on restore into a multi-pane layout.

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 working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions