Send to Kindle: name the reader in From, Reply-To them; doc@vapor.fyi as the reference sender - #107
Merged
Merged
Conversation
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… as the reference sender Resend only mails from a domain verified in the operator's account, so the reader's own address cannot be the From. Each message now goes out as "<Reader's name> via vapor" <SEND_FROM_EMAIL> with Reply-To set to the reader's email, so the approved-sender address stays constant while replies still reach the person who pressed Send. Anonymous or nameless senders fall back to the bare address. The reference instance sets SEND_FROM_EMAIL to doc@vapor.fyi; RESEND_API_KEY is a Workers secret and is set out of band. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
alcor
added a commit
that referenced
this pull request
Sep 13, 2026
…erialise from the editor (#108) ## Links open in a new tab A browser never follows a link inside editable content, so clicking a link in a document did nothing. The `AppLinks` plugin now handles every link in the text: `vapor:` links act as before (Agents panel, New document, Sign in), anything else opens in a new tab with `noopener,noreferrer`, and the document stays where it is. Taps on touch do the same. Links imported from markdown also rendered without `target`: the `richSchema` link mark stores `target: null`, and that null won over TipTap's `_blank` default at render time. The `Link` mark in `app/lib/app-links.ts` keeps `target` and `rel` on the mark for schema parity with the server but never renders them from it, so every link element carries `target="_blank" rel="noopener noreferrer nofollow"`. `@tiptap/extension-link` becomes a direct dependency at the version StarterKit already installs. ## Table text at 80% `.tiptap table { font-size: 0.8em }`. Tables carry more per line than prose; the smaller type keeps columns readable. Cells measure 12.88px against 16.1px prose in the dev server. ## Table cells serialise from the editor (found while verifying) `serializeCellInline` wrapped a cell's content in a `richSchema` paragraph. The editor serialises TipTap documents through the same path, and a paragraph from one schema refuses content from another, so on any document with a table every Yjs update threw inside the client's update handler (`Caught error while handling a Yjs update: Invalid content for node paragraph`). The client's markdown state then stayed empty: the tab title fell back to "vapor", the URL lost its slug, and anything else derived from that markdown went stale. The wrapper nodes now come from the cell's own schema. Dates from #27; separate commit. ## Verified Typecheck clean; 880 tests pass. Lint reports only the unused `READ` import on main that #107 removes. New tests: a click or tap on an ordinary link calls `window.open` with `_blank` and is prevented; plain text is left alone; a link stored with `target: null` renders `_blank` and its rel; a table from a second schema instance serialises unchanged. In the browser: the seeded document's link carries `target="_blank"`, a click attempts to open example.com in a new tab while the document tab stays put, cells render at 80%, and the tab title and slug now survive load on a document with a table. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #100 and #101.
Sender
Resend only mails from a domain verified in the operator's account, so a reader's own address cannot be the From. Instead each message goes out as
"<Reader's name> via vapor" <SEND_FROM_EMAIL>withreply_toset to the reader's email (from the session), so the approved-sender address readers add at Amazon never changes while replies still reach the person who pressed Send. Nameless or anonymous senders fall back to the bare address. Quotes, angle brackets, and line breaks are stripped from the name before it enters the header.handleDeviceRoutesgains adisplayName(principal)dep, wired to the Registry profile inworkers/app.ts, and passessenderthrough tosendKindle.Reference instance
deploy/vapor.fyi.jsoncsetsSEND_FROM_EMAILtodoc@vapor.fyi. Send to Kindle by email goes live onceRESEND_API_KEYis set on the Worker (npx wrangler secret put RESEND_API_KEY -c deploy/vapor.fyi.jsonc) andvapor.fyiis verified as a sending domain in Resend; until then the Kindle row keeps offering the EPUB download.Also
Drops the unused
READimport inagents/mcp.tsleft by #106, which failednpm run linton main.Verified
Typecheck and lint clean; 878 tests pass. New: From carries the name and Reply-To the email; bare address and no Reply-To when neither is known; header-unsafe characters stripped; the route passes the session's name and email as the sender.
🤖 Generated with Claude Code