Skip to content

fix(composer): keep an inline image where the user placed it - #1442

Merged
vastsa merged 1 commit into
mainfrom
fix/inline-image-order
Oct 7, 2026
Merged

vastsa merged 1 commit into
mainfrom
fix/inline-image-order

Conversation

@vastsa

@vastsa vastsa commented Oct 7, 2026

Copy link
Copy Markdown
Owner

Problem

An image chip keeps its place in the Composer draft, but on send the chip
serialized to nothing, so the image travelled as a trailing attachment. The
transcript and the provider prompt therefore put every image after the text
even when the draft had named it between words: text, image, text came back as
text, text, image.

Change

  • The Composer serializes an image chip to its own @path at the position it
    held; renderer-only sentinels never reach the prompt.
  • Electron main records that text as inlinePath on the prepared attachment
    (durable message attachment and sidecar attachment alike), and stops
    appending a fallback path the prompt already names.
  • One shared placement helper (packages/shared/src/inline-attachments.ts)
    splits the prompt into ordered text runs and attachments. The transcript
    renders the image chip there, a restored session rebuilds the same content
    blocks, and the runtime hands pi the built user message instead of
    prompt(text, images), which always appended images last.
  • An attachment the prompt does not name inline keeps the previous trailing
    behavior, and records written before this change render exactly as before.
  • Host-core persists inlinePath beside the existing attachment fields; data
    relocation never rewrites it (it is the user's own text).

Verification

  • pnpm test (build:js + every package test + cargo test -p host-core):
    desktop 3584, shared 1190, agent-runtime 1297, host-core 781 — all green.
  • pnpm --filter @pi-desktop/desktop typecheck, pnpm lint,
    node scripts/check-architecture.mjs, pnpm docs:check (85 locale pairs,
    559 pages), cargo fmt --check — all pass. Clippy reports only the two
    pre-existing transcripts.rs warnings.
  • New coverage: shared placement helper tests, prompt-inline-attachments.test.mjs
    (recording, dedupe, replayed copy), session-message-presentation.test.mjs
    (row order), and runtime.test.ts placement cases (live prompt, trailing
    image, restored history). test:e2e:composer-paste passes its Composer probe
    with imageOnly: true, including the updated submission assertion.
  • Specs and their zh-CN mirrors describe the placement; E2E-102 documents the
    ordering step.

An image chip serialized to nothing at submission, so an image travelled as a
trailing attachment: the transcript and the provider prompt put every image
after the text even when the draft had named it between words. The user's own
order survived editing and was then thrown away on send.

Serialize the chip's `@path` at its place in the prompt instead and have
Electron main record that text on the prepared attachment (`inlinePath`). The
durable message, a restored session, and the runtime's content blocks all read
the position back, so an image block sits where it was typed. An attachment the
prompt does not name inline keeps the previous trailing behavior, and records
written before this change render exactly as they did.

Host-core persists the new field beside the existing attachment fields; the
content block splice itself lives in one shared placement helper, so the
renderer and the runtime cannot disagree about where an image belongs.
Copilot AI balanced review requested due to automatic review settings October 7, 2026 01:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vastsa
vastsa merged commit 2e74898 into main Oct 7, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants