Translate from history via DeepL → fresh Compose draft
Pick an archived post → "translate to fr" in the detail view → its master_text
is translated by DeepL server-side and lands in Compose as a fresh draft with
the Bluesky/Mastodon language fields set to the chosen target. Confirms first
if there's an in-progress draft, mirroring the Reply/Quote hand-off.
- new internal/translate package: DeepL client (auto-detects Free vs Pro from
the API-key ':fx' suffix per DeepL spec), an ISO 639-1 → DeepL target_lang
allowlist of the 29 supported codes (lb/Luxembourgish notably absent), and
Intersect() to filter USER_LANGUAGES. Form-encoded request, JSON response;
covered by httptest stubs (free-vs-pro routing, pt→PT-PT mapping, error
surfacing, unsupported-target rejection, empty-text rejection).
- config: new DEEPL_API_KEY env (default unset). When unset, /api/translate
returns 503 and /api/config emits translate_targets:[] so the UI hides the
button — the feature is opt-in.
- api: new Translator interface (mirrors Verifier/Resolver pattern); new
POST /api/translate proxies to DeepL with the server-side key (never sent
to the client); GET /api/config now also returns translate_targets, the
intersection of USER_LANGUAGES and DeepL-supported codes preserving order.
- web: state.translateTargets populated from /api/config on boot; new
loadDraft({text,lang}) in compose.js confirms-before-replace, exits any
interaction mode, sets master + both lang fields, and switches tab; new
translateSelect() in history.js renders a small inline dropdown next to the
"text" header in the detail view (hidden when translate_targets is empty).
On pick: POST /api/translate → loadDraft.
Reply/quote in a full composer (Interact → Compose interaction mode)
Replying to or quoting an existing post now hands off from the Interact tab to
the Compose tab in "interaction mode": a source banner over the full composer,
so the reply/quote gets the live preview, auto-threading/splitting, media, and
per-platform overrides. Repost stays a one-click action in Interact.
- dispatch: runChain gains an optional head action (reply/quote head, tail
threads as replies); Interact reworked to one threaded chain per platform.
The source platform does the native action; selected other platforms fan out
a full reproduction — your commentary + the original's text + re-hosted media
+ link — assembled then threaded. Mastodon native-quote-with-media degrades to
reproduction. Per-platform text overrides honored.
- api: POST /api/interact is now multipart (spec + image files); the original's
media is re-hosted through an SSRF-guarded client (https-only, size-limited).
- web: Compose interaction mode (state.interaction, source banner, source-locked
+ fan-out platform chips, hidden scheduling, multipart Send); Interact hands
off reply/quote and keeps repost one-click. The v0.5.x inline action panel is
removed.
Normal posting, auto-threading, and resolve/preview are unchanged (additive).