# Edit-in-Word .docx round-trip — design

> Replaces the current .html round-trip so saving in Word "just works" without
> Save-As-Web-Page gymnastics. Adds a focus-back hint on save so the user
> doesn't have to Alt-Tab manually.

## Why

Today, Edit-in-Word writes `<editId>.html` and watches it. Word opens it but
when the user hits Ctrl+S, Word's default save format is **.docx**, not .html.
Word writes a *new* `<editId>.docx` file; the watcher misses the save; the
user's edits never reload into rmfmail.

Two options previously discussed (B watch-both-extensions; C inline hint).
Picking **A** here: pre-convert to .docx so Word's natural save flow lands in
the file we're watching.

## Pieces

1. **HTML → DOCX** at edit-start. Library: `html-to-docx` (Node, ~80KB,
   actively maintained). Output is a Node `Buffer`. Write to
   `~/.rmfmail/external-edit/<editId>.docx`.
2. **Watch `<editId>.docx`** instead of `.html`. Same `fs.watch(dir)` mechanic;
   filter by `<editId>.docx` rather than `<editId>.html`.
3. **DOCX → HTML** on save. Library: `mammoth` (Node, ~250KB, the de facto
   DOCX-to-HTML converter). Output is HTML string. Strip its outer wrapper.
   Emit `wordEditUpdated` as today.
4. **Focus rmfmail on save**. After parsing the docx, post a host-level
   "focus this window" message via msger. On Windows that's
   `SetForegroundWindow` on the WebView2 hwnd — msger already has the hwnd
   from window create. Add a `focusWindow` IPC method.
5. **In-compose external-edit indicator**. While `wordEditId` is set, the
   compose body editor area shows a non-editable status panel:

   ```
   ╭───────────────────────────────────────╮
   │ Editing in Word…                       │
   │ Save in Word → reloads here.           │
   │                                        │
   │  [ Reload now ]  [ Send ]  [ Discard ] │
   ╰───────────────────────────────────────╯
   ```

   - **Reload now**: read the .docx and parse manually (in case the watcher
     missed an event)
   - **Send**: parse current .docx, set as body, fire send (skips the
     intermediate "switch back to compose, click Send" step)
   - **Discard**: stop the watcher, delete the temp file, restore original body

## Library deps

| Library | Direction | Size | Notes |
|---|---|---|---|
| `html-to-docx` | HTML → DOCX | ~80KB | Pure JS Node. `await asBlob(html)` returns a Buffer. |
| `mammoth` | DOCX → HTML | ~250KB | De facto standard. Lossy on some Word features (text effects, complex tables) but covers email-shaped content. |

Total ~330KB added to mailx-service. Acceptable; only loaded on Edit-in-Word
click via dynamic `import()` so cold start cost is zero.

## What we lose

- Round-trip fidelity isn't perfect. Word's docx supports things mammoth
  doesn't (footnotes, comments, advanced styling). For email composition this
  is fine — users aren't writing footnotes — but worth knowing.
- Rich images: html-to-docx handles inline images (data: URIs) reasonably;
  mammoth round-trips them as `<img src="data:...">`. Should work but needs
  testing.

## Migration

- Existing watchers at `<editId>.html` keep working until the user reopens an
  Edit-in-Word session. New session uses .docx.
- Optionally clean up old `external-edit/*.html` files at startup.

## Implementation order

1. `npm install html-to-docx mammoth` in mailx-service.
2. Replace `.html` write at `mailx-service/index.ts:660` with .docx via
   html-to-docx.
3. Replace watcher target at line 745 with `<editId>.docx`.
4. Replace read+parse at line 755 with mammoth → HTML.
5. Add `focusWindow` IPC + msger hook.
6. Wire in-compose external-edit indicator panel (compose.ts).
7. Test with a Word install on Windows.
8. Skip Word path on Mac (Mac Word's docx behavior is the same; should work
   without changes — but verify).

## Open questions

- Does Mac Word treat .docx the same as Windows Word? (Should — same file
  format. Verify after impl.)
- LibreOffice fallback: opens .docx natively, saves to same .docx by default.
  No special handling needed.
- Default editor on Linux is usually LibreOffice; Word path n/a.

## Status

Design captured; implementation deferred until user confirms scope.
