Compose as new sits with Reply / Reply all / Forward in the per-message menu (MessageView), and after Forward in the list's right-click menu (MessageList). It opens a composer prefilled from the message: recipients (To, Cc and Bcc where the server kept them), Reply-To, subject with nothing prefixed, body with nothing wrapped around it, and attachments.
What makes it new
Mostly what it leaves out, and two of those are load-bearing:
draftId stays null.saveDraftInternal and sendInternal both add destroy: [draftId] — a draft that carried it would delete the message it was made from on send.
relatedEmailId / relatedKeyword stay null, so sending doesn't stamp $answered or $forwarded on the original.
inReplyTo and references stay null, so it threads onto nothing.
The Message-ID and date come free: buildEmailObject never sets messageId (the server assigns one) and stamps sentAt at build time. The send path needed no changes at all.
Judgment calls worth your eye
Identity. A message you sent is composed as the identity you sent it as (matching on from, the same rule that reopens a draft). For a message somebody else sent, the issue asks for the default identity, and I agreed — matching on who it was addressed to would put a resend behind an alias that was only ever the receiving end.
No signature is added, and signatureHtml is deliberately left empty. The body is the sent one, which already ends in whatever signature went out with it; appending the identity's would give it two. Same as reopening a draft.
Priority and read-receipt are carried, though the issue's "not to keep" list says "other headers". I read that list as being about headers identifying the old message — Message-ID, Date, threading. X-Priority and Disposition-Notification-To are the sender's intent, and the main use case is sending the same mail again, so reproducing them seemed right. Easy to drop if you disagree.
Verification
npm run typecheck, npm test (513 pass, 11 new in store/__tests__/compose-as-new.test.ts) and npm run build green. The tests pin what's kept and — more importantly — what's dropped, since a regression there is silent and destructive.
Driven by hand against dev:mock:
item present in both menus, in the right place
composer opens with the recipients, subject verbatim (no Re:/Fwd: added), body with no quote wrapper or forwarded-header block
a message from someone else → account default identity, as specified
invite.ics (371 B) carried across with its blob intact
sent it, and the original was still in Sent afterwards — the draftId property proven end to end rather than just unit-tested
Screenshot of the prefilled composer: attached in the conversation.
Not translated yet
One new string ("Compose as new") falls back to English in all nine locales.
Closes #176.
## What it does
**Compose as new** sits with Reply / Reply all / Forward in the per-message menu (`MessageView`), and after Forward in the list's right-click menu (`MessageList`). It opens a composer prefilled from the message: recipients (To, Cc **and** Bcc where the server kept them), Reply-To, subject with nothing prefixed, body with nothing wrapped around it, and attachments.
## What makes it *new*
Mostly what it leaves out, and two of those are load-bearing:
- **`draftId` stays null.** `saveDraftInternal` and `sendInternal` both add `destroy: [draftId]` — a draft that carried it would delete the message it was made from on send.
- **`relatedEmailId` / `relatedKeyword` stay null**, so sending doesn't stamp `$answered` or `$forwarded` on the original.
- `inReplyTo` and `references` stay null, so it threads onto nothing.
The Message-ID and date come free: `buildEmailObject` never sets `messageId` (the server assigns one) and stamps `sentAt` at build time. **The send path needed no changes at all.**
## Judgment calls worth your eye
**Identity.** A message you sent is composed as the identity you sent it as (matching on `from`, the same rule that reopens a draft). For a message somebody *else* sent, the issue asks for the default identity, and I agreed — matching on who it was addressed to would put a resend behind an alias that was only ever the receiving end.
**No signature is added,** and `signatureHtml` is deliberately left empty. The body is the sent one, which already ends in whatever signature went out with it; appending the identity's would give it two. Same as reopening a draft.
**Priority and read-receipt are carried**, though the issue's "not to keep" list says "other headers". I read that list as being about headers identifying the *old message* — Message-ID, Date, threading. `X-Priority` and `Disposition-Notification-To` are the sender's intent, and the main use case is sending the same mail again, so reproducing them seemed right. Easy to drop if you disagree.
## Verification
`npm run typecheck`, `npm test` (513 pass, 11 new in `store/__tests__/compose-as-new.test.ts`) and `npm run build` green. The tests pin what's kept and — more importantly — what's dropped, since a regression there is silent and destructive.
Driven by hand against `dev:mock`:
- item present in both menus, in the right place
- composer opens with the recipients, subject verbatim (no `Re:`/`Fwd:` added), body with no quote wrapper or forwarded-header block
- a message from someone else → account default identity, as specified
- `invite.ics` (371 B) carried across with its blob intact
- **sent it, and the original was still in Sent afterwards** — the `draftId` property proven end to end rather than just unit-tested
Screenshot of the prefilled composer: attached in the conversation.
## Not translated yet
One new string ("Compose as new") falls back to English in all nine locales.
**Merged** 2026-09-01 as coffey-labs/ihasmail@5a7acb7306d0
<sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #176.
What it does
Compose as new sits with Reply / Reply all / Forward in the per-message menu (
MessageView), and after Forward in the list's right-click menu (MessageList). It opens a composer prefilled from the message: recipients (To, Cc and Bcc where the server kept them), Reply-To, subject with nothing prefixed, body with nothing wrapped around it, and attachments.What makes it new
Mostly what it leaves out, and two of those are load-bearing:
draftIdstays null.saveDraftInternalandsendInternalboth adddestroy: [draftId]— a draft that carried it would delete the message it was made from on send.relatedEmailId/relatedKeywordstay null, so sending doesn't stamp$answeredor$forwardedon the original.inReplyToandreferencesstay null, so it threads onto nothing.The Message-ID and date come free:
buildEmailObjectnever setsmessageId(the server assigns one) and stampssentAtat build time. The send path needed no changes at all.Judgment calls worth your eye
Identity. A message you sent is composed as the identity you sent it as (matching on
from, the same rule that reopens a draft). For a message somebody else sent, the issue asks for the default identity, and I agreed — matching on who it was addressed to would put a resend behind an alias that was only ever the receiving end.No signature is added, and
signatureHtmlis deliberately left empty. The body is the sent one, which already ends in whatever signature went out with it; appending the identity's would give it two. Same as reopening a draft.Priority and read-receipt are carried, though the issue's "not to keep" list says "other headers". I read that list as being about headers identifying the old message — Message-ID, Date, threading.
X-PriorityandDisposition-Notification-Toare the sender's intent, and the main use case is sending the same mail again, so reproducing them seemed right. Easy to drop if you disagree.Verification
npm run typecheck,npm test(513 pass, 11 new instore/__tests__/compose-as-new.test.ts) andnpm run buildgreen. The tests pin what's kept and — more importantly — what's dropped, since a regression there is silent and destructive.Driven by hand against
dev:mock:Re:/Fwd:added), body with no quote wrapper or forwarded-header blockinvite.ics(371 B) carried across with its blob intactdraftIdproperty proven end to end rather than just unit-testedScreenshot of the prefilled composer: attached in the conversation.
Not translated yet
One new string ("Compose as new") falls back to English in all nine locales.
Merged 2026-09-01 as coffey-labs/ihasmail@5a7acb7306
Rebuilt from: git history, session transcript.