Skip to content

Typing becomes slow with a very tall image attached to the composer #10361

Description

@MaximilianMauroner

Problem

Typing in the composer becomes noticeably slow when a very tall PNG is attached. Typing works normally in other threads.

Observed on macOS, T3 Code Nightly 0.0.39-nightly.20260906.1293.

The affected image was:

  • Dimensions: 2304 × 32766 pixels, approximately 75.5 megapixels.
  • PNG file size: 5,967,750 bytes.
  • Estimated decoded RGBA size: 288 MiB, before any additional browser or GPU copies.

The original image and all personal data are excluded from this report.

Reproduction

  1. Create a synthetic PNG with dimensions 2304 × 32766. Use varied content so the compressed file is approximately 6 MB. A solid-color PNG can test decoded-image pressure but will not reproduce the same storage cost.
  2. Open a new thread and attach the PNG.
  3. Type continuously, then type with pauses longer than 300 ms.
  4. Observe whether characters appear late or typing stalls.
  5. Compare with a draft without attachments and with a resized copy of the same synthetic image.

For a stronger comparison, test with background tasks idle and record a renderer performance trace during typing.

Expected: Typing stays responsive regardless of attachment dimensions.

Actual: The draft containing the large image has substantial input lag. Other threads remain responsive.

Investigation

Source maps shipped with the affected build show two relevant paths:

  1. The thumbnail uses the original image URL. In ChatComposer.tsx, the expanded attachment preview renders <img src={image.previewUrl}> inside a 64 × 64 container. This path does not itself generate a smaller thumbnail. The source dimensions create potential decode and rendering pressure despite the small displayed preview.

  2. Draft persistence includes the image payload. The affected draft stores the PNG as a base64 data URL, approximately 7.96 MB. In composerDraftStore.ts, prompt edits update the draft store. Persistence serializes the persisted draft state, including attachments, through JSON.stringify. In lib/storage.ts, createDeferredStorage writes it to synchronous localStorage after a 300 ms debounce.

The debounce reduces write frequency, but the serialization and storage write still run on the renderer thread. It does not make those operations asynchronous.

These are plausible causes, not yet isolated by a controlled image-removal test. The persistence path covers the whole draft store, so it could also affect other threads while the image-bearing draft remains saved.

Measurement limits

Diagnostics recorded renderer peaks of 94.1% CPU and 791 MB resident memory during the investigation window. These were not isolated to typing and do not establish causation.

Background directory scans were also active during part of the investigation and could have contributed to overall load. A short native renderer sample did not identify the responsible JavaScript function.

UI automation call timings are not valid measurements of key-to-paint latency and are intentionally omitted.

Suggested fix

  • Generate a bounded-resolution thumbnail for attachment previews and keep the original separately for sending.
  • Store attachment blobs separately from prompt text, preferably in asynchronous storage or the existing attachment-upload system.
  • Persist attachment references with the draft so prompt edits do not serialize the full image again.
  • Keep thumbnail generation and image conversion out of the per-keystroke render path.
  • Use a targeted renderer trace to determine whether decoding, painting, serialization, or storage dominates.

Validation

Compare no attachment, a normal screenshot, and the synthetic tall PNG. Check continuous typing and typing with pauses longer than 300 ms.

Verify that:

  • Typing does not repeatedly decode the original or rewrite its base64 payload.
  • Input latency remains close to the no-attachment case.
  • Draft text and attachments survive navigation and restart.
  • Sending still uses the original image.

Prepared with GPT-6 using Codex.

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 is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions