Changelog
Every feature, improvement, and fix — tracked as we build OxygenPDF.
v2026.07.09-01
Forgot your password? You can reset it yourself now
The sign-in form has a 'Forgot password?' link. We email you an 8-digit code, you pick a new password, and you're signed in. No support ticket needed. One thing worth knowing if you bought a license: your license key already works as your password. It starts with OPDF- and sits in your purchase receipt email, so you may not need a reset at all. The sign-in form, the reset screen, and the reset email all point this out now.
All changes in version 2026.07.09-01
- NewReset a forgotten password with a code sent to your email
- NewSign-in reminds license buyers that their license key doubles as their password
v2026.06.17-01
June 17, 2026The app reloads itself after an update instead of throwing an error
If you kept a tab open while we shipped a new version, OxygenPDF could reach for a file from the old build that no longer exists and show a 'Something went wrong' screen — the loading-animation stylesheet was the usual culprit. It now catches that case and quietly refreshes to the current version, dropping you back where you were. Sign-in got a fix too: password managers like iCloud Passwords now put your saved email and password straight into the login form.
All changes in version 2026.06.17-01
- FixedStale tabs reload on their own after a deploy instead of crashing with 'Unable to preload CSS'
- FixedSaved logins from iCloud Passwords and other password managers now autofill the sign-in form
v2026.06.15-02
June 15, 2026Type any language into your PDFs — umlauts, accents, and non-Latin scripts now work
Adding a watermark, header, footer, or page stamp — or turning text, Markdown, Word, and CSV into a PDF — used to quietly break on characters outside basic Latin. German umlauts typed on a Mac were the worst offender: the diacritic comes through as a separate combining mark that the old font could not encode, so the whole export failed. The text tools now embed real Unicode fonts and normalize your text first, so umlauts, accented names, Cyrillic, Greek, and CJK (Chinese, Japanese, Korean) render properly. Arabic, Hebrew, Hindi, and Thai are supported too, with right-to-left and complex-script shaping handled on a best-effort basis. The same fix reaches the PDF editor and chat form-filling, and scanned documents in non-Latin scripts now keep a searchable text layer instead of dropping it. Fonts for non-Latin scripts download only when needed and are cached; your files never leave your browser.
All changes in version 2026.06.15-02
- FixedDecomposed umlauts (ä, ö, ü typed on macOS) no longer break PDF text export
- NewWatermarks, headers, footers, page numbers, and Bates stamps accept any-language text
- NewText, Markdown, Word, and CSV to PDF render Latin, Cyrillic, Greek, and CJK scripts
- NewOCR keeps non-Latin scripts in the searchable text layer instead of stripping them
- NewPDF editor annotations and chat form-filling accept umlauts and non-Latin scripts
v2026.06.15-01
June 15, 2026Speed read Markdown, text files, and pasted text — not just PDFs
The RSVP speed reader now opens .md and .txt files alongside PDFs, or you can paste raw text straight in from a new tab. Markdown gets its syntax stripped first — headings, links, bold, lists — so you read the actual prose instead of flashing past # and ** one word at a time. Plain text is split into sections so the preview panel and jump-to-page keep working on long documents. It all still runs in your browser; nothing is uploaded.
All changes in version 2026.06.15-01
- NewRSVP speed reader accepts Markdown (.md) and plain text (.txt) files
- NewPaste raw text directly into the RSVP speed reader
- NewMarkdown syntax is stripped to clean prose before reading
v2026.05.13-01
May 13, 2026Switch tiers when you regenerate a visual webpage
The regenerate button on a visual-HTML chip used to silently re-run with the same quality tier you originally picked, which meant if Basic came out flat you had to start a new conversation to try Best. The button is now a small menu — pick Basic, Quality, or Best directly from the chip and the new build runs at the tier you chose (and the chip subtitle updates to match so you can tell at a glance which tier produced the result). The cache is always busted on regenerate, so a tier you already tried genuinely re-runs against the model rather than handing back a stale result.
Memory chips don't pop up on pleasantries anymore
The assistant occasionally over-extracted a "Save to thread memory?" chip when you replied with just "thanks" or "ok cool" — fabricating a preference you never actually stated. The chat prompt now hard-bans memory suggestions on social acks and explicitly forbids inventing facts the user hasn't spoken, and a server-side filter drops any memory chip that lands on a pleasantry-shaped turn regardless of what the model emitted. Saying thanks no longer triggers a memory pill.
All changes in version 2026.05.13-01
- NewChip regenerate menu lets you re-run the visual HTML at Basic / Quality / Best
- NewMemory chips suppressed on short pleasantry messages (thanks, ok, hi, etc.)
- FixedRegenerate at a different tier now updates the chip subtitle to reflect the new tier
v2026.05.11-14
May 11, 2026Pick your quality tier when converting a PDF to a visual webpage
Visual HTML recreation now asks one extra question after you pick 'Visual recreation': Basic, Quality, or Best? Each level runs through a different AI model behind the scenes — Basic is fastest and cheapest, Best is slowest but reads design-heavy pages the most carefully. The chip subtitle shows which tier produced the result so you know what you're paying for, and the regenerate button reuses the same tier you originally picked. Replaces the previous three-pass refinement loop (which was producing modest gains for a lot of extra cost). The underlying prompt also got a full rewrite — the AI now produces Tailwind-CDN-based responsive HTML with semantic tags and proper design fidelity instead of hand-rolled blocky CSS with US-Letter page dimensions.
Chat clarifications never dead-end on generic fallback chips anymore
When you asked the assistant to convert a PDF to Word or HTML, it sometimes wrote "let me ask a couple of quick questions" but forgot to actually emit the wizard — the bubble would just sit there with the four generic follow-up chips (Explain more / Give an example / …) underneath, which read as a broken UI. The model now gets a hard prompt rule against promising clarifications without emitting the matching directive, and a server-side safety net catches the misfire by inferring the right wizard format from your message and recovering the wizard inline.
All changes in version 2026.05.11-14
- FixedNew inferExportWizardFromMisfire safety net in chatPdf.ts runs when no directive was honored. Matches few-shot prose patterns ("let me ask…", "let me walk you through…", "pick the right…", "couple of (quick )?questions to pick") against the user's question (html/website → html, docx/word/.doc(x) → docx) and injects the recovered <export-wizard format/> into the message patch. Telemetry tag wizard-recovered lands on emittedDirectives so we can monitor the rate.
- FixedSystem prompt gains an INVERSE-RULE clause forbidding clarification prose without the matching directive. Calls out the exact phrase shapes ("let me ask…", "let me walk you through…", "pick the right…") that must co-occur with <export-wizard/> or <ask-choice>. Also re-asserts that image-based PDFs asked for docx/html STILL route through the wizard, not <ask-choice>.
- ImprovedVisual HTML rebuild swapped from Gemini Pro to Replicate-hosted openai/gpt-5.4. callReplicateVisionHtml() posts to /v1/models/openai/gpt-5.4/predictions using Replicate's FLAT input schema — `prompt` (text instruction) + `image_input` (array of client-rendered page-image URIs) at the top level, plus reasoning_effort / verbosity / max_completion_tokens sampling knobs. Important gotcha encoded in the comments + memory: Replicate's openai/* wrappers do NOT accept OpenAI's `messages` array or `type: "file"` content parts; sending those gets the entire payload silently dropped by the input validator (symptom: 'I don't have the PDF' replies). Hence the render-pdf-pages.ts + attachVisionHtmlPageImages pipeline that pre-rasterizes to JPEG before upload. awaitReplicateOutput drains via Prefer wait + 2s polling, capped at 180s per pass. Requires REPLICATE_API_TOKEN env var. Hard caps: first 10 pages, 32k output tokens per pass. Bump REPLICATE_VISION_HTML_MODEL to a newer minor revision when Replicate ships one.
- NewClient-side PDF rasterization for the visual flow — new render-pdf-pages.ts uses pdfjs-dist at 1.5× scale + JPEG quality 0.85 to render each page (max 10) into a Blob, the VisionHtmlChip uploads each one via Convex generateUploadUrl, and the new attachVisionHtmlPageImages mutation patches the resulting storage IDs onto pdfIndex.visionHtmlPageImageStorageIds. getVisionHtmlDownloadUrl now exposes pageImagesReady + pdfSourceUrl + pageRenderLimit so the chip can skip re-rasterization when the cache is warm. Adds a "Preparing page images for the vision model…" subtitle state to the chip during the prep phase.
- FixedVisionHtmlChip regenerate button now defensively preps page images when they're not attached. Older pdfIndex rows created before visionHtmlPageImageStorageIds existed in the schema (or any row whose cached status='ready' / 'error' predates the Replicate switch) hit startVisionHtmlPass with no images cached, which threw 'Page images not yet attached'. handleRegenerate now mirrors the auto-start + handleClick guard: if pageImagesReady is false, render+upload+attach first, then force-regenerate.
- New3-tier quality picker for the visual-match flow. VISION_HTML_MODEL_BY_TIER maps each tier to a different Replicate-hosted vision model — basic→prunaai/gemma-4-26b-a4b-fast, quality→google/gemini-3.1-pro, best→anthropic/claude-opus-4.7. New chatMessage.visualQualityTier + pdfIndex.visionHtmlTier schema fields persist the choice per-message and gate cache validity (a tier-mismatch on the cache forces a re-run). Wizard adds a second-step picker after 'Visual recreation' (Basic / Quality / Best with descriptive hints, NEVER showing the underlying model). applyExportWizardChoice / startVisionHtmlPass / runVisionHtmlPass all accept tier args. Chip subtitle now reads 'Building (Quality quality)…' / 'Website · Quality · click to download'. New buildVisionInput(modelSlug, …) helper dispatches the request body shape per provider — each Replicate wrapper has its own flat schema with different field names, so a unified body silently drops fields and the model hallucinates. Verified schemas via scraping each /api page's embedded OpenAPI definition: openai/* uses image_input array + max_completion_tokens + reasoning_effort + verbosity; google/* uses images PLURAL array + max_output_tokens + thinking_level; prunaai/* (Gemma 4) uses message (NOT prompt) + images plural + max_tokens (cap 16384) + max_visual_tokens (pinned to 1120 — without this, images auto-compress to 280 vision tokens and the model hallucinates); anthropic/* uses image singular + max_tokens (cap 64000) + system_prompt + max_image_resolution (set to 2 MP, otherwise images auto-downscale to 0.5 MP). Anthropic and Gemma only accept ONE image per call, so multi-page PDFs on those tiers lose pages 2..N.
- ImprovedRemoved the critique-then-apply iteration loop and its supporting scaffolding (VISION_HTML_CRITIQUE_PROMPT, VISION_HTML_REFINE_PROMPT, VISION_HTML_MAX_ITERATIONS, callReplicateVisionHtmlCritique, callReplicateVisionHtmlApply). Visual-match is now single-pass — one model call per visual recreation. The cost reduction (5x to 1x calls) funds the model upgrades in the tier picker; a High-tier single pass on gpt-5.4 produces materially better single-shot output than 5 stacked passes on gpt-5-mini did.
- ImprovedVISION_HTML_PROMPT rewritten end-to-end. Old prompt explicitly banned Tailwind and any CSS framework, and framed the task as converting a PDF (no mention of the attached image) — producing hand-rolled CSS with US-Letter 8.5in by 11in page boxes and Open Sans defaults. New prompt opens with a directive that the attached images ARE page screenshots (primes the model to actually look at the inputs), mandates Tailwind CSS via CDN script, semantic HTML5 elements, mobile-first responsive breakpoints, WCAG AA contrast, arbitrary-value Tailwind syntax for off-scale colors and radii. Preserves the two non-negotiable platform contracts: section.pdf-page[data-page=N] wrappers + pN-imgK image placeholder syntax so inlineImagesAsDataUrls still substitutes Mistral-extracted images. Adds placehold.co URL convention for synthetic decorative placeholders that have no source image counterpart.
v2026.05.11-13
May 11, 2026Inline export wizard replaces the flat ask-choice for Word and HTML conversions
When you asked to convert a PDF to Word or HTML, the chat assistant used to show a flat list of 3-4 options you had to read and commit to in one click. Re-deciding meant editing your message or starting over. The new wizard walks you through 1-2 short questions inline in the bubble — back-navigable, fully frontend (no extra AI round-trips per question), and the final pick writes the right export shape directly to the message. Markdown and text still skip the wizard since there's only one sensible output for those.
All changes in version 2026.05.11-13
- NewNew <export-wizard format="docx|html|markdown|text"/> directive. Self-closing, mutually exclusive with emit-file/ask-choice/open-tool/fill-pdf/run-action. Parser in chatPdf.ts validates the format against a closed set; the dispatcher places it between run-action and ask-choice in priority order so the model can hedge with the wizard when it would otherwise emit ask-choice.
- NewNew ExportWizard component (apps/main/src/features/chat-pdf/components/export-wizard.tsx). Renders inline inside the assistant bubble when message.exportWizardFormat is set. 2-step state machine for HTML (priority → edit-style branch), 1-step for DOCX (priority directly to outcome). History stack for back navigation; submit goes through applyExportWizardChoice mutation, no AI round-trip per step.
- NewNew applyExportWizardChoice mutation clears exportWizardFormat and sets emittedFileFormat/Source/Filename in one patch. Validates format against EMIT_FILE_FORMATS and source against {index, index-visual}; rejects index-visual for non-HTML. Optional proseSummary updates the bubble text so the wizard can leave a contextual note about which chip to click.
- NewchatMessage gains exportWizardFormat (optional string). _patchMessage accepts it. Bubble rendering shows the ExportWizard component below EmittedFileChip, hides the FollowUpChips while the wizard is active (matching the existing ask-choice contract).
- ImprovedSystem prompt reroutes all docx/html conversion requests through <export-wizard/> instead of direct <emit-file source="index"/> or <ask-choice> trees. The flat 4-option HTML ask-choice and 3-option DOCX ask-choice examples are gone. Markdown and text still emit emit-file directly since there's only one viable output for each.
v2026.05.11-12
May 11, 2026"Generate a fresh website that visually matches" — fourth option for scanned-PDF HTML
When a scanned PDF gets a 'give me as html' request, the chat assistant now offers four routes instead of three. The new option uses a vision AI pass to rebuild the design as a real webpage with CSS — colors, gradients, custom typography, the works — instead of stripping it down to a semantic export. Click it and the screenshot-to-code pass kicks off automatically: no second click required, the chip shows a spinner while it works, then flips to a download when ready.
All changes in version 2026.05.11-12
- NewNew EmitFileSource value `index-visual` routes HTML exports straight to the screenshot-to-code chip. parseEmitFilePlan accepts the new value only for format=html — typo-rejects anything else so a model fumble doesn't surface as an empty download. EmittedFileChip renders only the VisionHtmlChip when source is index-visual, with auto-start.
- NewVisionHtmlChip gains an `autoStart` prop. When true, a useEffect-guarded by a single-fire ref triggers startVisionHtmlPass on mount as soon as the query resolves and status is null. Strict-mode double-effects can't double-charge; ready/running/error states all skip the auto-fire.
- NewSystem prompt now has an explicit 4-option ask-choice for scanned PDFs requesting HTML, plus follow-up routing rules for each option. Visual-match option emits <emit-file format="html" source="index-visual"/>; OCR-text option emits the standard <emit-file format="html" source="index"/>. Model behavior is now deterministic instead of improvised from the Word pattern.
- ImprovedRemoved the Replicate VLM chip and backend in their entirety — VisionReplicateChip component, runReplicateVisionHtmlPass action, startReplicateVisionHtmlPass mutation, getReplicateVisionHtmlDownloadUrl query, generatePageImageUploadUrl mutation, callReplicateVisionHtml / fetchReplicateLatestVersion / flattenReplicateTextOutput helpers, getReplicateToken, REPLICATE_API_BASE / REPLICATE_DEFAULT_VISION_MODEL constants, and the pdfIndex.visionReplicate* schema fields. The open VLMs on Replicate hard-cap their output at 512 tokens — too small for screenshot-to-code at any quality. Kept the GEMINI path and added the index-visual route in its place.
v2026.05.11-11
May 11, 2026Replicate-powered Website chip — open VLM as a second opinion
Gemini 2.5 Pro is strong on screenshot-to-code but it makes specific mistakes some PDFs trip over. The new Replicate chip routes the same task through Qwen2.5-VL by default — different model class, different failure modes, sometimes a better fit for a given design. Sits alongside the Gemini chip so you can A/B the two on the same PDF without re-uploading. Backend is parameterized via env vars so swapping to DeepSeek-VL3 or any other Replicate VLM takes a config change, not a redeploy.
All changes in version 2026.05.11-11
- NewNew runReplicateVisionHtmlPass internal action calls Replicate with one image per page (parallel) and aggregates the resulting fragments into one self-contained .html. Model/version env-overridable via REPLICATE_VISION_MODEL / REPLICATE_VISION_VERSION; defaults to lucataco/qwen2-vl-7b-instruct. Image-inlining reuses the same pN-imgK → base64 data URL post-processor as the Gemini path.
- NewNew generatePageImageUploadUrl mutation returns Convex signed upload URLs so the frontend can render PDF pages to PNG client-side and stream them up in parallel before kicking off the action. Replicate doesn't ingest PDFs the way Gemini does, hence the extra client-side step.
- NewpdfIndex grows visionReplicateStorageId + visionReplicateStatus + visionReplicateErrorMessage + visionReplicateModel. The model name on the index lets the chip display which Replicate model produced the output so users know what they got. Same idempotency contract: cache hit on second click per index.
- NewVisionReplicateChip renders alongside VisionHtmlChip for <emit-file format="html" source="index"/> messages. Amber accent, Cpu icon to distinguish it from Gemini's fuchsia Globe. Click → render pages client-side → upload → trigger action → poll → download `<base>-replicate.html`.
- ImprovedAction drops the uploaded page PNGs from Convex storage after the .html is produced — they served their purpose, the final output is self-contained, no reason to keep them eating storage quota.
v2026.05.11-10
May 11, 2026Website (visual) chip — screenshot-to-code for design-heavy PDFs
Word documents can't render modern CSS, which means infographics, posters, and Canva-style exports always lose their visual chrome on a Word export — no matter how clever the converter. HTML can carry all of it. The new chip sends the source PDF to Gemini 2.5 Pro and asks for self-contained HTML+CSS that visually recreates the design: gradients, custom typography, decorative shapes, layered illustrations. Cropped images get inlined as base64 data URLs so the .html opens in any browser with nothing broken. Sits next to the standard HTML chip so you can pick semantic-text or visual-reconstruction depending on what you need.
All changes in version 2026.05.11-10
- NewNew runVisionHtmlPass internal action calls Gemini 2.5 Pro (Pro tier, not Flash — visual reconstruction needs the larger model) with the PDF as inline data and a screenshot-to-code prompt. Output gets code-fence-stripped, then every `<img src="pN-imgK">` reference is replaced with a base64 data URL pulled from the Tier-1 stored images. Self-contained .html is stored in Convex storage.
- NewpdfIndex grows visionHtmlStorageId + visionHtmlStatus + visionHtmlErrorMessage, parallel to the existing markdown vision-pass fields. Same idempotent contract: cache hit on second click, charges chat_pdf_vision_html ops only on the first uncached invocation per index.
- NewVisionHtmlChip renders alongside the standard HTML chip for any <emit-file format="html" source="index"/> message. Globe icon, fuchsia accent, idle → pending/running → ready/error state machine driven by getVisionHtmlDownloadUrl. Downloaded file is named `<base>-visual.html`.
- FixedrunLayoutPreservedPdfToWord now throws a clear error when the PDF has zero extractable text (Canva / Figma / Illustrator exports outline text into vector paths — pdfjs returns nothing). Message points users at the Website (visual) chip instead of returning a silently empty .docx.
- ImprovedGemini call helper extracted from callGeminiVision into callGemini(pdfBytes, model, prompt) so the new HTML pass can reuse the same 20 MB size guard, base64 encoding, and error handling. callGeminiVision becomes a thin wrapper that pins Flash + the markdown prompt.
v2026.05.11-9
May 11, 2026Word (layout) chip — text in the right positions, editable
The previous "page-perfect" chip just embedded each PDF page as a PNG inside the docx. Better than nothing visually, but you couldn't edit, copy, or search anything. The new converter extracts every text item from the PDF via pdfjs with its real (x, y) coordinates and emits one absolutely-positioned paragraph per item in a docx sized to match the source. Font size and bold/italic come along. Result: text lands where it belongs, and you can edit it. v1 ships text only; image extraction and colors land in follow-ups.
All changes in version 2026.05.11-9
- NewNew layout-preserved.ts converter walks pdfjs textContent in viewport space (top-left origin), extracts transform[4]/[5] as baseline x/y in points, derives font size from transform[0], and detects bold/italic via fontFamily regex on the styles map. Each item becomes a docx Paragraph with frame.type='absolute' anchored to FrameAnchorType.PAGE. Per-page sections sized to viewport dimensions with zero margins so frame coords map 1:1 to source.
- FixedPagePerfectChip swapped from runPdfPagesToWord (screenshot-in-docx) to runLayoutPreservedPdfToWord. Subtitle updated to "layout-preserved" so the chip honestly describes the tradeoff: text in the right positions, editable, but v1 doesn't yet carry images or vector chrome.
v2026.05.11-8
May 11, 2026Word (page-perfect) chip — pixel-identical to the source PDF
Some PDFs are infographics, posters, designed layouts where every arrow, gradient, and font choice matters. Word documents can't reproduce that as vector content — the decorative chrome dies in any text-based conversion. The new chip skips the OCR pipeline entirely: each PDF page renders to a high-resolution image client-side and embeds in the docx as a full-page picture. The result looks identical to the source. Trade-off is honest: editable text is gone, but visual fidelity is total. Sits next to the other two chips so you can pick the one that matches the document.
All changes in version 2026.05.11-8
- NewNew PagePerfectChip surfaces alongside the standard Word chip for any <emit-file format="docx" source="index"/> message. Fetches the source PDF via getSourcePdfDownloadUrlForMessage, runs runPdfPagesToWord client-side (pdfjs render at 2x DPI → PNG → ImageRun → docx), downloads as `<name>-pages.docx`. Reuses existing infra from the scanned-PDF flow; we just stopped gating it on pdfType.
- FixedThree-chip layout (Editable / Page-perfect / High quality) gives users an honest tradeoff matrix: editable text vs. visual fidelity vs. better layout reconstruction. Each chip uses a distinct icon and accent (FileText/blue, ImageIcon/emerald, Sparkles/violet) so they read as separate options rather than redundant duplicates.
v2026.05.11-7
May 11, 2026Chat PDF gets a "Word (high quality)" chip that reads layout the way you do
Mistral OCR is fast and cheap but it flattens multi-column layouts, breaks callout boxes, and treats key-value pairs as random sentence fragments. The new chip hands the whole PDF to Gemini 2.5 Flash and asks for cleanly structured Markdown — heading hierarchy, columns interleaved in reading order, tables reconstructed as tables, callouts as block quotes. It sits next to the standard Word chip so you can pick the cheap fast path or the better slower one depending on the document. Picks up the same embedded images Tier 1 added, so figures still land in the right spots.
All changes in version 2026.05.11-7
- NewNew runVisionLayoutPass internal action calls Gemini 2.5 Flash with the source PDF as inline data and a structured-Markdown prompt. PDF-native ingest means no per-page rendering on our side; 20 MB inline cap surfaces as a clean user-visible error rather than a half-truncated document.
- NewpdfIndex grows visionMarkdownStorageId + visionStatus (pending/running/ready/error) + visionErrorMessage. The vision pass output caches per index, so a second click on the high-quality chip downloads the existing result instead of re-charging the user.
- NewstartVisionLayoutPass mutation kicks off the action with a charge of pageCount ops against chat_pdf_vision_hq. Idempotent: if a pass is already pending/running/ready, returns the existing status with no re-trigger and no re-charge.
- NewVisionHQChip component renders alongside the standard IndexedExportChip for docx exports. Walks through idle → pending/running (spinner) → ready (download) → error (retry) states driven by getVisionMarkdownDownloadUrl. Sparkles icon and violet accent so users see the two chips as distinct options instead of duplicates.
- NewGemini placeholders like  get post-processed to point at the Tier-1 image refKey for the K-th image on page N (resolved via the new pageIndex field on pdfIndexImage). Unmatched placeholders fall through and the converter drops them — same contract as the rest of the image pipeline.
v2026.05.11-6
May 11, 2026Chat PDF exports stop leaking ![img-0.jpeg] text where pictures should be
Convert an infographic-style PDF to Word in chat and you used to get the headings and paragraphs right but every image came out as literal markdown placeholder text like `` floating between the paragraphs. The OCR pipeline was returning image bboxes but throwing the cropped image bytes away, and the docx converter had no `image` token handler so the marked AST default echoed the raw markdown into the output. Now the OCR pass pulls the cropped image bytes inline, stores them per-image, and the docx/html converters embed them where they belong. Reupload the same PDF and the index gets a free one-time re-OCR so existing chats benefit too.
All changes in version 2026.05.11-6
- FixedMistral OCR call now sets include_image_base64: true so cropped pictures come back inline. Each image lands in Convex storage with its bbox dimensions tracked in a new pdfIndexImage table keyed by a globally-unique refKey (page number plus original image id). Per-page collisions on ids like `img-0.jpeg` no longer scramble which page's image gets fetched.
- NewrunOcr rewrites each page's markdown so image refs become globally unique within an index. `` on page 3 turns into `` before the markdown is stored — downstream conversion no longer has to track which page section a ref came from.
- NewgetIndexedMarkdownDownloadUrl returns signed URLs for every cropped image alongside the markdown URL. The download chip pre-fetches them in parallel and hands the bytes to the docx converter via a resolver callback; HTML export rewrites markdown image hrefs to the signed URLs pre-parse so the resulting <img> tags load.
- NewcreateWordDocumentFromMarkdown grows an optional resolveImage option. The `image` marked token now routes through it instead of falling to the raw-text default, so unresolved refs drop silently rather than echoing `` into the .docx. Image-only paragraphs become standalone ImageRun blocks with generous spacing.
- NewNew pdfIndex.imagesIngested flag drives a one-time backfill: existing indexes from before image ingest cache-miss on re-upload and re-OCR without a double charge, same contract as the prior needsBlockUpgrade backfill.
- Fixedheadless-image-paths.ts canvasToImageRun was missing the required `type` field on docx v9 ImageRun (would've thrown at runtime). Set to 'png' to match the canvasToBlob output.
v2026.05.11-5
May 11, 2026Chat PDF exports stop ignoring the OCR markdown they already had
Ask Chat to convert a PDF to Word and you used to get a wall of unstyled text — headings flattened, paragraphs collapsed, plus whatever OCR typos were baked into the source PDF's text layer. Meanwhile every PDF you upload already runs through Mistral OCR during indexing, which produces clean markdown with real headings, lists, and tables. The conversion path was just ignoring it. Now it doesn't. Ask for .docx, .html, .md, or .txt and the download chip rebuilds the file from the indexed markdown. Heading 1s stay Heading 1s, tables survive, bold and italic stick, and nothing inherits the broken OCR sitting in the source bytes.
All changes in version 2026.05.11-5
- NewNew <emit-file source="index"/> directive shape — self-closing, no body. The chat assistant emits it for conversion requests, the chip pulls pdfIndex.markdownStorageId at download time, and conversion to docx / html / markdown / text happens client-side. Replaces the lossy <open-tool slug="pdf-to-word"/> path for TextBased and Mixed PDFs.
- NewEMIT_FILE_FORMATS expanded from markdown/text to markdown/text/html/docx. Inline-source emit-file still only accepts markdown/text — the model can't write a docx body inline. Index-source supports all four since the body comes from storage.
- NewNew getIndexedMarkdownDownloadUrl query returns a signed URL to the indexed markdown, gated by message → conversation → user ownership. The emitted-file chip dispatches on emittedFileSource: inline streams the stored blob as before; index fetches the markdown, strips internal page separators and cite markers, and runs format-specific conversion (marked for HTML, createWordDocumentFromMarkdown for DOCX). Both converters lazy-load so the chat bundle doesn't pay for marked/docx on every page load.
- FixedChat system prompt updated: "convert to word" on a TextBased or Mixed PDF routes through <emit-file format="docx" source="index"/> instead of <open-tool slug="pdf-to-word"/>. The OCR-then-Word branch in the scanned-PDF ask-choice routes through the index path too — Mistral OCR already ran during ingest, re-OCRing in a separate tool was duplicate work.
- NewSchema: chatMessage gets emittedFileSource ("inline" | "index") so the chip knows whether to stream a stored blob or convert from indexed markdown. Existing inline rows keep working — the field defaults to inline when undefined.
v2026.05.11-4
May 11, 2026Chat PDF asks before converting a scanned PDF to Word
Ask Chat to convert a scanned PDF to Word and you'd previously get back an empty .docx with no warning. Now Chat notices the source is image-only and asks under the reply: run OCR and pack the text into a fresh Word doc, embed each page as an image (preserves the look, not editable), or do both — text alongside each page image. Click a chip and the file is built and ready to download in the same bubble. No bouncing into a separate tool. No flipping a toggle to enable OCR. Just pick the outcome and the AI delivers it.
All changes in version 2026.05.11-4
- NewNew <ask-choice> directive the chat assistant emits when running the obvious tool would silently fail. The model writes a prompt and 2–4 labeled options; the bubble renders each option as a button that submits its label as a fresh user turn, so the AI's next response routes on what the user actually picked. Mutex with the other action directives in the same turn — picking the wrong tool is worse than pausing to ask. Reusable: future failure modes like password-protected PDFs or multi-language scans can opt into the same chip-style clarification.
- NewThree new chat-only conversion slugs: ocr-pdf-to-word (OCR → editable Word), pdf-pages-to-word (each page embedded as an image — preserves layout), and ocr-pdf-to-word-with-images (both — OCR text alongside each page image). Each has its own headlessRun so the chat chip downloads the right artifact in one shot, no flipping into the standalone tool to toggle settings.
- FixedThe model was leaking the Document profile into its prose ("This PDF contains selectable text, so I can route you directly..."). Renamed the prompt section to "Routing hints (INTERNAL — never mention, quote, or paraphrase in your prose)" and added explicit examples of terse acks. The bubble now reads "Converting to Word." with no meta-commentary on what the AI thinks the PDF is.
- NewPDF classification on upload. The chat upload hook fires inspector.classify() in the background after the bytes land and patches pdfType (TextBased / Scanned / ImageBased / Mixed) plus hasExtractableText onto the pdfIndex row. The system prompt renders it as a Document profile block, which is what lets the LLM tell the difference between a text PDF and a scan before it decides which directive to emit.
- FixedHeadless pdf-to-word throws EmptyExtractionError when extraction returns under 8 trimmed characters instead of packing an empty .docx. The chat chip catches it and falls back to the click-to-open CTA so the user can still get into the standalone PDF→Word tool (which has OCR mode in its config panel) rather than walking away with a zero-byte download.
- NewSchema additions: pdfIndex gets pdfType, hasExtractableText, pagesRequiringOcr; chatMessage gets askChoicePrompt + askChoiceOptions. New chatPdf.setDocProfile mutation persists the client classification, and a new parseAskChoicePlan parser clamps option counts and label lengths before any chip lands on the message.
- FixedCache-hit rows backfill the profile on the fly. Legacy pdfIndex rows from before this field existed pick up the classification the next time they're opened in a chat, so the model eventually sees a profile for every document without a separate migration job.
v2026.05.11-3
May 11, 2026Chat PDF can fill IRS-style forms now
Ask Chat to fill an IRS Form SS-4 (or any PDF whose AcroForm field names look like topmostSubform[0].Page1[0].f1_1[0]) and it actually works. The server tries to match labels by name first since that path is cheap and handles most forms. If nothing matches, the browser takes over: pdfjs reads the visible label text plus every form-widget rectangle on the page, then pairs each label with the closest empty field nearby. Same chip, same download. The old "I couldn't match any of those labels" failure should be a lot rarer.
All changes in version 2026.05.11-3
- FixedChat PDF form fill: when name-based AcroForm matching produces zero hits, hand the labels off to the browser instead of writing an error. A new unresolvedFillFields field on chatMessage stages the unmatched labels, isFilling stays true so the chip keeps pulsing while client-side grounding runs.
- NewClient-side spatial grounding for form fields. The new resolve-fill-fields helper reads pdfjs text content and widget annotations per page, finds the label's bbox in PDF user space, and scores nearby unfilled widgets. Same-row pairings (label on the left, field on the right) win; a below-label fallback catches stacked layouts. One widget per label, greedy in input order, threshold-guarded so a label without a plausible nearby widget is left alone.
- NewNew applyResolvedFill public action on chatPdfFill. Takes {fieldName, value} pairs the browser already resolved, applies them by exact AcroForm name, regenerates appearance streams, and lands the filled blob on the message via the same _setMessageFill mutation runFormFill uses. Idempotent: a duplicate dispatch from React strict-mode hits the alreadyFilled guard and returns early.
- NewNew useApplyResolvedFill hook in the thread pane. Watches the chat list for messages with unresolvedFillFields set but no filledPdfStorageId yet, downloads the effective source PDF (respecting the conversation's prior fill chain), runs the spatial grounding, dispatches applyResolvedFill. Tracks in-flight message ids so the reactive query re-emitting a row can't spawn a parallel resolve.
- FixedChat PDF form fill: route "Yes" / "No" answers to checkboxes instead of leaking them into nearby text fields. The resolver now detects boolean values (yes / y / true / on / 1 — and their negative counterparts) and only considers checkbox widgets for those, while text values are barred from checkbox widgets. Stops the misfill mode where "yes" got written into the "If 8a is Yes, enter number of LLC members" text field on SS-4.
- NewCheckbox disambiguation by adjacent option label. Each checkbox widget gets tagged with the nearest "yes" or "no" text item within ~24pt of its center, so the resolver can pair {label: "Is this an LLC?", value: "Yes"} to the checkbox the form actually printed "Yes" next to (not whichever box happens to be closer to the question text). Radio groups still untouched in this pass.
- FixedResolver emits a console.warn listing every label it couldn't place — visible per-fill, useful for diagnosing model output drift (e.g. when the LLM emits "Company name" instead of "Legal name" and the form's text layer doesn't carry that synonym).
v2026.05.11-2
May 11, 2026Chat PDF now has a real memory
Chat PDF carries facts forward across turns. Three scopes: things about you (your name, role, how you like answers), things about the PDF (who the contract is for, what kind of report this is), and things about the chat (bullet-only answers, skip the jargon). The assistant proposes saves itself when something durable comes up — you get a Save/Dismiss chip on the bubble, nothing lands without you saying yes. Or type /remember anything you want kept; /memories opens the panel; /forget cleans up. Memories ride into every future prompt so you stop repeating yourself.
All changes in version 2026.05.11-2
- NewChat PDF memory system. New chatMemory table on the backend with three scopes (user / document / thread), each indexed for ownership-checked retrieval. The chat pipeline pulls the applicable rows on every turn and renders them as a "Saved memories you must respect" section above the document context — within a hard 2000-char injection budget so power users with 50+ memories don't bloat the prompt.
- NewChat PDF: new <remember scope="user|document|thread">FACT</remember> directive. ADDITIVE — the model can emit it alongside any other directive, since saving a fact is orthogonal to whichever action drives the turn. The suggestion stages on the assistant message (pendingMemoryText/Scope) and renders as a Save/Dismiss chip in the bubble; nothing lands in chatMemory until the user clicks Save. Dismiss clears the pending fields with no row written. The model is told to only emit for durable facts about the person, the document, or stable preferences — never for one-off question content.
- NewSlash commands: /remember <text> (defaults to user scope, --document and --thread override), /forget <n>, /forget all [scope], /memories. Parsed client-side so they don't burn LLM tokens or count against the monthly operation cap. Synthetic user + assistant turns are inserted so the thread shows what got saved or forgotten. Dedupes against existing exact-match memories at the same scope so spamming /remember can't bloat the table.
- NewMemories panel in the workspace top bar. Grouped by scope with inline edit / delete affordances. Pulls from the same Convex query the chip components subscribe to, so accepting a suggestion on a bubble or running /remember updates the panel in the same render.
v2026.05.11
May 11, 2026Chat PDF now delivers filled markdown as a real downloadable file
Ask "fill this with mock data and give me the markdown file" and you get exactly that: the filled content rendered inline AND attached as a downloadable .md (or .txt) chip — no more misleading "Filled PDF" artifact when you explicitly named a different format. Powered by a new <emit-file> directive on the chat router: when the user names a text output format, the model wraps the synthesized content and the server stores it as a blob, so the download chip lands on the message the same way the PDF chip does. PDF fill requests are unchanged.
All changes in version 2026.05.11
- NewChat PDF: new <emit-file format="markdown|text" filename="…">CONTENT</emit-file> directive. The chat router parses the tag inside the model response, stores the captured content as a blob (text/markdown or text/plain), and attaches it to the assistant message via emittedFileStorageId/emittedFileFilename/emittedFileFormat. A new EmittedFileChip renders alongside FilledPdfChip in the bubble with a Download CTA. emit-file outranks the other three directives in honor priority (emit > run > open > fill) because a named output format is the most specific user intent.
- FixedChat PDF: stop emitting <fill-pdf> when the user explicitly asks for a non-PDF output format. The system prompt now routes "markdown file" / ".md" / "save as text" through <emit-file> instead, and word-doc/image requests get a prose-only response with no directive (no path to those formats yet from the chat router).
v2026.05.10-8
May 10, 2026Filled-form values now land inline with their labels
When you converted a filled fillable PDF, the values you typed showed up at the corner of each form field instead of where the text actually sits — so labels and values collided in weird ways and the checkbox glyphs ended up clustered at the bottom of the doc. Fixed: pdf-inspector now reads each widget's appearance stream (the same data pdftotext extracts) and places the rendered text at its real position. "Please enter your name: Jordan Lee" reads as one line, not two; checkboxes appear next to the option they belong to; values inside tables sit in their cells.
All changes in version 2026.05.10-8
- Newpdf-inspector: extract widget appearance streams. New extract_widget_appearance_text walks each page's /Annots, follows every Tx/Ch widget's /AP/N to its Form XObject, computes the placement transform mapping the form's /BBox (post-/Matrix) onto the widget's /Rect, and runs the appearance content stream through the existing extract_form_xobject_text pipeline. Result: form values land at their actual draw position as ordinary positioned text items, the same way pdftotext sees them.
- Fixedpdf-inspector AcroForm /V walker now skips widgets whose appearance stream already produced text (so values aren't double-emitted). Btn fields stay on the /V walker path because their appearance is usually a single Zapf Dingbats glyph; the ☑/☐ rendering reads better than the raw extracted character.
- FixedPer-page Y-descending re-sort on pages that contain widget annotations. Without it, the line builder's stream-order vs Y-sort heuristic flipped on filled-form pages — appearance items jumping back up to their widget positions read as chaos and form values were emitted at the document tail. Pages without widgets are untouched, so non-form PDFs (where stream order can be load-bearing for footnotes/sidebars) aren't affected.
- FixedTables in fillable forms now render as real Word tables, not flat paragraphs. Three fixes that compose: (1) the thin-rect synthesis path now merges co-linear segments before passing to the line-based table detector, so Word's per-cell border stamping (each cell edge as its own 0.48pt rect) reconstructs into the 3 horizontal + 3 vertical lines a 2×2 table needs — previously each 14.64pt fragment was discarded as decoration. (2) the line-based detector skips its capture-ratio gate when cell density is ≥80%, so densely-filled small tables embedded in form pages aren't rejected as chart-axis pseudo-tables. (3) cell assignment iterates column edges right-to-left so items right at a boundary land in the higher column instead of the lower one (the prior find returned the first match, mis-claiming items inside the ±2pt boundary tolerance).
- Newpdf-inspector now tracks per-glyph fill color (g/rg/k/sc/scn operators) and propagates it through extraction and the merge pass. TextItem gained a color field exposed to JS via the WASM API, used by two markdown layer changes: (a) force a paragraph break between adjacent lines whose dominant color differs by ≥32 per RGB channel, so blue section labels stay separated from black body values; (b) promote short non-near-black lines to H2 headings, which Word renders in blue by default — restoring the visual style of fillable-form labels without needing inline color spans in the markdown.
- FixedCheckbox options now render as a bulleted list, one option per paragraph, instead of collapsing onto a single line. Three sub-fixes: (1) is_list_item / format_list_item recognize ☑ and ☐ as list markers; (2) the line builder no longer breaks the link between a glyph and its label when the glyph sits to the left of the label (which routinely happens because the widget's Rect.x is a few points lower than the page-content label's x — the line-builder's reverse-x check now permits single-character items to join their row); (3) the list formatter inserts a space between glyph and label when the source has none.
- FixedTightened paragraph spacing on Heading2 (240/120 → 120/60 twips before/after) and body paragraphs (200 → 120 twips after). Form-style PDFs sit labels close to their values; the previous Word defaults pushed them too far apart and made the docx look airy compared to the source.
- FixedStop bolding the first table row by default. Markdown table syntax always has a header row, but the dependent-style tables our inspector synthesizes from thin-rect borders don't actually want bold styling — row 1 is just data. Render every row identically; if the source has a real header, the user can bold it in Word.
- FixedSet Calibri as the docx body font on every TextRun. Word's default fallback is Times New Roman in some renderers; explicit Calibri matches what most Word-generated source PDFs use.
- FixedCheckbox lines no longer get a Word bullet stacked on top of the ☑/☐ glyph. The list-paragraph generator skips the bullet when an item starts with a checkbox glyph, so each option reads as just ☑ Option 1 — matching the visual style of the source form.
- FixedTighter Heading2 spacing — after-spacing zeroed so a blue label sits flush against the value paragraph beneath it, the way form-style PDFs render. Body paragraph after-spacing trimmed from 120 to 80 twips, list-item after-spacing zeroed.
v2026.05.10-7
May 10, 2026Filled-form PDF to Word: no more "Dropdown2:" in your docx
Convert a filled fillable PDF and the docx came back with internal field identifiers all over it — "Name: Jordan Lee", "Dropdown2: Choice 1", "Option1 Option 1: On", "NameofDependent Name of Dependent: Alex Lee". Those are PDF-internal names that Word users were never meant to see. They're gone now: only the value lands in the doc, at the field's position. Checkboxes also got upgraded — instead of "Option 1: On" you get a real ☑, and unchecked boxes show ☐ instead of being silently dropped. Same fix flows through pdf-to-markdown and the chat-pdf headless converter.
All changes in version 2026.05.10-7
- Fixedpdf-inspector form-field walker (extractor/links.rs): drop the "FieldName: " prefix when emitting AcroForm values. /T names like "Dropdown2", "Option 1", "Name of Dependent" are dev-facing identifiers — they have no place in user-visible output. Page content already has the human label.
- FixedBtn fields now render as Unicode glyphs: ☑ for /On, ☐ for /Off (and for missing /V). Previously /Off returned early so unchecked boxes left a hole in the output, and /On emitted the literal string "On". Pushbuttons (/Ff bit 17) are skipped — their /V is an action target, not a checkbox state.
- Fixedpdf-inspector markdown post-processor: normalize literal tab characters in extracted text to single spaces. Word/Acrobat-generated PDFs sometimes encode inter-word positioning gaps as \t, which renders as nothing in Word without a tab stop, mashing words together ("Name of Dependent" → "NameofDependent" in the output docx). Replacement happens after table cells are emitted, so markdown tables (which use pipes) are unaffected.
v2026.05.10-6
May 10, 2026Chat PDF: filled-in form text no longer comes back as gibberish
If you fed Chat PDF a filled form and asked for a Word or Markdown export, the values you actually typed (names, addresses, anything non-trivial) came out as ?? J o r d a n L e e — replacement chars where the BOM should have been, and a space wedged between every letter. That was a UTF-16 BE byte order mark the form-field decoder was reading as raw UTF-8. Acrobat and Preview both write filled values that way, so basically every real-world filled form was hitting this. Fixed: the decoder now handles both PDFDocEncoding and UTF-16 BE, the way the PDF spec says it should.
All changes in version 2026.05.10-6
- Fixedpdf-inspector (Rust/WASM): form-field /T names, /V text values, and link /URI strings now route through decode_text_string instead of String::from_utf8_lossy. Detects the 0xFE 0xFF BOM, decodes the rest as big-endian UTF-16; falls back to PDFDocEncoding for plain bytes.
- FixedSame path fixes pdf-to-markdown and pdf-to-word in chat (both ran through the inspector) and any tool that reads AcroForm values
v2026.05.10-5
May 10, 2026Chat PDF: ask, get the file. No tool overlay needed.
Telling the assistant "convert this to a word doc" used to land you on a "Open in PDF to Word" chip — fine, but you still had to click through, wait for the tool to mount, and click Convert. Now: when you ask, the chip pulses with "Converting to Word…" and resolves to a downloadable .docx right in the bubble. Same for JPG, PNG, WebP, BMP, plain text, Markdown, grayscale, color invert, repair, and annotation removal. Everything that has a deterministic transform with no parameters now auto-executes the moment the model suggests it. Tools that genuinely need UI input (sign, redact regions, image watermark, edit-pdf) still show the click-to-open chip — that's where it's actually useful.
All changes in version 2026.05.10-5
- NewTool registry: optional `headlessRun: () => Promise<HeadlessRunFn>` on ToolDefinition lets a tool register a UI-free executor that takes a File and returns a {blob, filename}. Lazy import keeps the conversion deps out of every host bundle.
- NewChat: SuggestedToolChip auto-executes a tool's headlessRun when present. Chip lifecycle: idle → running ("Converting to {tool}…") → ready (download chip with the produced blob). Falls back to the original click-to-open chip if the headless path errors so users are never stuck.
- NewHeadless wired for ten tools: pdf-to-word, pdf-to-jpg/png/webp/bmp (shared image-export pipeline), pdf-to-text, pdf-to-markdown, repair-pdf, remove-annotations, pdf-to-grayscale, invert-colors
- ImprovedExtracted shared makePdfToImageHeadless helper for the four pdf-to-image tools — single render-to-canvas-and-zip pipeline, format/quality/extension as params. Per-tool wrappers are 3 lines.
v2026.05.10-4
May 10, 2026Chat PDF: instant rotate, lock, sanitize, compress, reverse
The chat assistant can now run zero-config transforms directly without bouncing you through a tool. Say "rotate every page 90", "lock the form", "compress this", "remove the metadata", or "reverse the page order" and the result downloads in the bubble — same chip you already get for form fills, no second click required. Anything more involved (conversions, signing, redaction, image watermarks, page extraction) still routes through Open-in-tool, so you only pay the UI tax when there's actually configuration to do.
All changes in version 2026.05.10-4
- NewChat: new <run-action type="..."/> directive lets the AI fire rotate / reverse / flatten / sanitize / compress directly on the source PDF — chip pulses while the transform runs, then resolves to a download chip with the appropriate filename suffix
- NewBackend: new chatPdfFill.runInitialAction internalAction loads the conversation's source PDF, applies one of five shared pdf-lib transforms (extracted from the existing follow-up actions), and stores the result via _setMessageFill — same chip semantics as form fill
- ImprovedBackend: extracted applyFlatten / applySanitize / applyRotate / applyReverse / applyCompress as shared pdf-lib transforms; both the chip's More-menu follow-up actions and the new run-action directive call them. Single source of truth for transform behavior
- NewBackend: directive telemetry — every assistant message records emittedDirectives (everything the model tried) and honoredDirective (what we acted on) so prompt-fidelity drift shows up in the data instead of as user-visible bugs
- NewSystem prompt now lists three directives (fill / run / open) with priority rules and 11 few-shot examples covering rotate, reverse, lock, sanitize, compress, convert, extract, redact, sign, add page numbers, and the explicit "no directive" case
v2026.05.10-3
May 10, 2026Chat PDF: ask in plain English, get the right tool
The chat assistant used to know how to do exactly two things — answer questions and fill forms. Anything else (convert to Word, rotate pages, compress, sign, redact, watermark, crop, extract pages…) it would punt on with "I can't do that." No more. The system prompt now ships with the full catalog of fifty PDF-input tools, and the assistant emits an <open-tool> directive whenever you ask for an action it can't run inline. The reply gets a "Open in PDF to Word" (or whichever tool fits) chip — click it and the tool opens fullscreen over the chat with your PDF already loaded. As a side effect: the model can no longer over-eagerly emit <fill-pdf> on a non-fill turn, because <open-tool> takes precedence when both appear, killing the stray "Filled PDF" chip that used to show up after a botched conversion request.
All changes in version 2026.05.10-3
- NewChat: AI now suggests the right tool for any operation it can't run inline — emits <open-tool slug="..."/> directive that the chip resolves to a one-click "Open in {tool}" CTA, mounting the tool over chat with the indexed PDF pre-staged on continuation
- NewBackend: system prompt rewritten with the full PDF-input tool catalog (fifty tools across Organize / Edit / Convert / Security / Optimize), explicit directive rules (mutually exclusive <fill-pdf> vs <open-tool>), and few-shot examples so the model picks the right slug for "convert to Word", "rotate", "redact", etc
- NewBackend: new chatMessage.suggestedToolSlug field + getSourcePdfDownloadUrlForMessage query; parseOpenToolPlan validates against the catalog so a hallucinated slug never persists
- FixedChat: stray "Filled PDF" chip on conversion requests — when the model over-emitted <fill-pdf> with hallucinated labels even though the user asked to convert, the dispatcher now prefers <open-tool> when both appear and the prompt explicitly forbids fill-pdf as a fallback for unrelated asks
v2026.05.10-2
May 10, 2026Chat PDF: every tool, one ellipsis away
The More menu on the download chip used to be a fixed shortlist — Edit, Rotate, Reverse, Lock, Sanitize, Compress. Now it has a search input at the top and the full OxygenPDF catalog underneath, grouped by category (Organize, Edit, Convert, Security, Optimize). Pick any tool and it opens fullscreen over the chat with your filled PDF already loaded — no re-upload, no losing the conversation. The original quick actions stay at the top for one-tap operations; the catalog is for when you want the full tool experience (page-by-page rotation, watermark options, redaction, etc).
All changes in version 2026.05.10-2
- NewChat: download chip's More menu picks up a Search input and the full PDF-input tool catalog grouped by category — every tool that accepts a PDF is one click away
- NewChat: new generic ToolOverlay mounts any registry tool fullscreen over chat with the filled PDF pre-staged on the continuation context (mirrors the existing EditPdfOverlay plumbing — useLayoutEffect + staged gate so the tool consumes continuation before its first paint)
- NewChat: catalog items search across title + category + description, so typing 'convert' surfaces every Convert tool and 'compress' finds Compress PDF wherever it lives
v2026.05.10
May 10, 2026Chat PDF: in-page editor + a fuller More menu
The "Edit in editor" item on the download chip now opens the existing /tools/edit-pdf experience as a fullscreen overlay over the chat instead of navigating away — your conversation stays put underneath. The More menu also picked up three new actions: Rotate 90° (clockwise per click, stacks for 180°/270°), Reverse page order, and Compress (structural). The chip's label and filename track every action you apply, so a fully worked-over PDF reads as e.g. "Filled, rotated, locked, and compressed PDF" — and once an action has been applied, its menu item flips to a "done" state to keep you from doing it twice.
All changes in version 2026.05.10
- NewChat: "Edit in editor" opens the editor inline as a fullscreen overlay (CloseTabProvider trick lets EditorDialog render inline inside our portal'd container instead of stacking another fullscreen Dialog on top)
- NewChat: More menu adds Rotate 90° (composes with prior rotation), Reverse page order, and Compress (structural). Menu now grouped into Continue editing / Structure / Finalize / Optimize
- NewBackend: chatPdfFill gains rotateFilledPdf, reverseFilledPdf, and compressFilledPdf public actions sharing the runFollowUpAction helper. Save options gate per-action ({useObjectStreams, objectsPerStream} for compress)
- NewChat: chip filename and label track six action types now (filled, rotated, reordered, locked, sanitized, compressed) with proper Oxford-comma serialization
v2026.05.09-7
May 9, 2026Chat PDF: ellipsis menu on the download chip — Edit, Lock, Sanitize
The filled-PDF download chip picked up a "More" ellipsis modeled on the continuation tool's tool-picker. Open it on a filled chip and you get three follow-up actions: Edit drops the file into /edit-pdf via the continuation context (no re-upload), Lock flattens the form so the recipient can't change your answers, and Sanitize strips the PDF's identifying metadata (author, title, producer, timestamps). The chip's label and filename update to reflect what's been applied — "Filled and locked PDF", "Filled, locked, and sanitized PDF" — so the user always sees what they're about to download. Each follow-up action is one Convex action call: load the existing filled PDF, transform with pdf-lib, replace the storage blob, patch the message.
All changes in version 2026.05.09-7
- NewChat: download chip gains a More ellipsis (mirrors the continuation tool-picker) with Edit in editor, Lock the form, and Sanitize metadata
- NewChat: Edit action hands the filled PDF to /edit-pdf through the existing continuation context — the editor consumes the blob on mount instead of bouncing the user through the dropzone
- NewChat: Lock action runs pdf-lib form.flatten() on the server and replaces the chip's filled PDF in place; chip label flips to "Filled and locked PDF"
- NewChat: Sanitize action strips Title / Author / Subject / Keywords / Creator / Producer / CreationDate / ModDate from the filled PDF's Info dictionary
- NewBackend: new public Convex actions chatPdfFill.flattenFilledPdf and chatPdfFill.sanitizeFilledPdf, plus internal helpers (_getMessageForFollowUp, _replaceMessageFilledPdf, _setMessageFollowUpPending / Error)
v2026.05.09-6
May 9, 2026Chat PDF: ask the assistant to fill the form, get a downloadable PDF back
Drop a fillable PDF into chat, hand the assistant the values you want plugged in, and a download chip appears in its reply with the form actually filled. The assistant emits a structured fill-pdf directive alongside its prose answer; the server loads the original PDF, fuzzy-matches each label to a real AcroForm field with pdf-lib, applies the values, and stores the filled document. The bubble shows a pulsing chip while it works, then resolves to a tappable card that downloads the result with a sensible filename. Forms without AcroForm fields, or labels we couldn’t match to anything on the page, surface a quiet warning instead of silently failing.
All changes in version 2026.05.09-6
- NewChat: AI can now produce a downloadable filled PDF when the user asks to complete the form — emits a <fill-pdf> directive that the server applies via pdf-lib AcroForm fuzzy-matching and returns as a stored file
- NewChat: assistant bubble gains a download chip for filled PDFs with three states — pulsing "filling…" placeholder, tappable download card with filename, or an inline warning when the form has no fillable fields or no labels matched
- NewBackend: new chatPdfFill action (Node runtime) loads the original PDF from Convex storage, runs fuzzy label-to-field matching with bigram similarity, applies values to text/checkbox/radio/dropdown/option-list fields, and stores the result
- NewBackend: chatMessage rows gain isFilling / filledPdfStorageId / filledPdfFilename / fillError fields; new getFilledPdfDownloadUrl query returns an ownership-checked signed URL for the bubble
v2026.05.09-5
May 9, 2026Chat PDF: warm paper canvas, editorial AI replies, deep-teal you
The chat workspace got a top-to-bottom design pass. The flat all-white canvas became a warm paper-and-ink surface system: the gutter behind the PDF is now a soft cream, the page itself stays paper-white as the protagonist, and the chat panel sits on a slightly raised warm offwhite. AI answers no longer wear a chat-bubble — they read as editorial prose on a Newsreader serif body with a subtle accent rail down the side, and citation chips became small italic footnote numerals instead of bright blue badges. Your messages flip to a deep-teal bubble with a pinched corner so the asymmetry visually says "I asked, the document answered." The sidebar recesses into the canvas, active chats get a left bookmark-ribbon and a raised paper card, and the empty-state hero now opens with an oversized italic "a" behind "Ask the document anything." Composer pill picks up an inset highlight, the send button is a circular deep-teal disc that nudges on hover. Same panes, same shortcuts, very different emotional temperature.
All changes in version 2026.05.09-5
- NewChat: warm paper-and-ink surface tokens (chat-pdf-canvas / surface / sidebar / page / accent / accent-soft / accent-rail) with layered warm-tinted shadows and dark-mode equivalents
- NewChat: AI replies render as editorial prose with Newsreader serif body, accent left rail, and italic footnote-style citation chips (no more bubble fill on assistant messages)
- NewChat: user message bubble switches to deep teal with a pinched bottom-right corner so the user/AI asymmetry reads as conversation, not symmetric chat balloons
- NewChat: sidebar drops into the recessed-canvas tone; active conversation now gets a left accent ribbon and a raised paper card; section labels render as Newsreader italic
- NewChat: empty state opens with an oversized italic "a" drop-letter behind "Ask the document anything," prompt cards become outlined paper cards on accent-soft hover
- ImprovedChat: composer pill carries an inset highlight on the warm canvas, the send button becomes a circular deep-teal disc with a hover nudge, and the indexing progress bar/status chips switch from bright blue to the new accent system
- NewChat (landing): the upload page rebuilds on the warm paper surface — eyebrow becomes a Newsreader italic line with accent dots, headline gains an italic teal "PDF." accent and a drop-letter motif, and the dashed dropzone becomes a raised paper card with an accent-soft upload medallion that lifts on hover
- NewChat (landing): "Recent chats" gets an italic serif label flanked by a hairline divider, and the list card sits on the canvas with a raised paper shadow
- ImprovedChat: conversation row hover swaps to a translucent rule wash so it reads on both the sidebar surface and the lighter landing card without disappearing
v2026.05.09-4
May 9, 2026All changes in version 2026.05.09-4
- FixedChat PDF: tooltips on the selection toolbar (highlight swatches, bookmark) no longer get clipped by the toolbar — the shared Tooltip now portals to the body so it can escape any overflow-hidden ancestor
v2026.05.09-3
May 9, 2026Chat workspace: top bar with layout switch, theme + sign-in always one click away
A new top bar sits above the preview and chat panes. A three-way switch flips between PDF only, split view, and chat only — picks stick across reloads. Theme toggle and your account live up there too, alongside a keyboard-shortcuts shortcut. The old "Indexed — ask anything about this PDF" line was replaced with a small "✦ Ready" chip beside the filename, and a thin progress bar shows up only while the document is still indexing.
All changes in version 2026.05.09-3
- NewChat: top bar with PDF / split / chat layout switch, theme toggle, account button, and shortcuts hint
- NewChat: layout choice persists across reloads and auto-uncollapses when a citation chip is clicked
- NewChat (mobile): theme toggle and account button now reachable from the mobile header
- ImprovedChat: replaced the "Indexed — ask anything about this PDF" status line with a compact chip beside the filename + progress bar that only shows while indexing
v2026.05.09-2
May 9, 2026Chat workspace: collapsible sidebar, resizable chat pane, fewer keystrokes
The left sidebar now has a collapse button (or ⌘B), and the chat thread on the right is draggable — pull the divider to give the PDF more room or the chat more room. Your width sticks across reloads. The "New chat" shortcut moved off ⌘K (which usually means "search" in apps that have it) to ⌘⇧O, matching ChatGPT and Claude.
All changes in version 2026.05.09-2
- NewChat: draggable vertical divider between PDF preview and chat thread, with the chosen width persisted to localStorage
- NewChat: collapse button on the desktop sidebar and a floating expand button on the left edge when collapsed
- NewChat: highlights and bookmarks track the canvas across viewport resizes — annotations no longer drift off the text on window resize
- FixedChat: highlights and bookmarks are now persisted on the indexed PDF and re-render on reload, with a right-margin fallback marker if the bbox cannot be re-resolved
- ImprovedChat: replaced ⌘K with ⌘⇧O for "New chat" so ⌘K stays free for a future command palette
v2026.05.09-1
May 9, 2026Select any passage in a chat PDF to explain, highlight, or bookmark it
Drag across text in the preview and a small toolbar appears above it. Tap Explain or Summarize and the selection goes to the AI as a quoted question. Pick a color to highlight the passage, or drop a bookmark to come back to it later. Both stick to the document, and a "Highlights & bookmarks" button at the top of the preview lists everything you have saved — click any entry to jump straight to its page. Right-click still works the same way for keyboard people. Replies from the AI now end with follow-up chips (Explain more, Give an example, Show source, Summarize this) so you can keep going without inventing a new prompt every turn.
All changes in version 2026.05.09-1
- NewChat PDF: floating selection toolbar with Explain, Summarize, Define, Highlight (4 colors), Bookmark, and a custom Ask, triggered by drag-select or right-click
- NewChat PDF: highlights and bookmarks persist per indexed PDF and render on top of the preview, with a delete affordance on hover
- NewChat PDF: "Highlights & bookmarks" panel in the preview header lists every saved annotation with click-to-jump and inline delete
- NewChat PDF: assistant replies show static follow-up chips so the next question is one tap away
v2026.05.09
May 9, 2026Chat citations now highlight the cited sentence, not the whole block
Click a citation chip and you used to get the whole chunk, sometimes a multi-paragraph slab full of facts that had nothing to do with the answer. Citations now carry a verbatim quote, and the preview pane lands the highlight on that exact sentence. Two different chips on the same chunk hit two different sentences. When the quote can't be found (paraphrase, image-only PDF), the highlight drops back to the old chunk flash so you still see the right block.
All changes in version 2026.05.09
- NewChat: citation chips highlight the cited sentence. The model attaches a verbatim `q="..."` quote, the preview pane resolves it to a tight bbox instead of the whole chunk.
- ImprovedChat: citation tag parsing switched to an order-independent attribute scanner, so new cite attributes survive whatever order the model picks
- FixedChat: two chips pointing at the same chunk no longer share one cached bbox. The resolution cache is keyed by `(elementId, quote)`, so sibling citations land on their own sentences
v2026.05.08-13
May 8, 2026Chat with PDF — mobile, keyboard, and a polished workspace
The chat workspace got a refresh. On phones, a tab toggle at the top swaps between the PDF preview and the chat thread, and tapping any citation chip flips you to the PDF so you actually see the source. The composer is a rounded pill with an embedded send button, an auto-growing textarea, and a kbd-hint footer. The empty state now offers four one-tap starter prompts. Recents in the sidebar bucket into Today, Yesterday, This week, This month, and Earlier. Keyboard shortcuts: ⌘K for new chat, ⌘B to toggle the sidebar, / to focus the composer, ? for the full reference. Hit ? anywhere on the page to see them.
All changes in version 2026.05.08-13
- NewChat: mobile tab switcher between PDF preview and chat thread; citation chips auto-flip to the PDF
- NewChat: rounded-pill composer with auto-growing textarea, embedded send button, and kbd-hint footer
- NewChat: empty state surfaces four starter prompts that pre-fill the composer with one tap
- NewChat: keyboard shortcuts — ⌘K new chat, ⌘B toggle sidebar, / focus composer, ? open shortcut help
- NewChat: recents in the sidebar are now bucketed by Today / Yesterday / This week / This month / Earlier
- ImprovedChat: thread pane split into ChatComposer + ChatEmptyState components to stay inside the 100-line component cap
- ImprovedChat: workspace shell rebuilt around a mobile-first three-pane layout with a Sheet drawer for the sidebar
v2026.05.08-12
May 8, 2026Chat with PDF — sidebar, edit, regenerate
The chat workspace now has a left rail with your recent conversations, a one-click way to start a new chat, and a back link to the rest of OxygenPDF — no more URL surgery to switch threads. Hover any user message to edit and resend it, hover the latest assistant reply to regenerate it. Copy lives on every bubble. Both edit and regenerate truncate the thread cleanly at the fork point so the conversation stays coherent.
All changes in version 2026.05.08-12
- NewChat: workspace sidebar with recent conversations, "New chat", and a back link to the home page
- NewChat: edit any user message and regenerate the assistant reply — backend `editAndResend` action truncates the thread at the edited message and re-runs retrieval
- NewChat: regenerate the latest assistant response — backend `regenerateResponse` action finds the preceding user question, deletes the stale reply, and re-runs the prompt
- NewChat: copy any message to clipboard via a hover-revealed action button
- NewChat: delete a conversation from the sidebar (gated behind a confirmation dialog)
- NewChat landing page now lists recent conversations below the dropzone
- ImprovedChat thread pane split into `chat-message-bubble`, `chat-message-actions`, and `chat-assistant-body` to stay under the 100-line component cap
v2026.05.08-11
May 8, 2026Chat with PDF — selection no longer balloons to the top of the page
Selecting a paragraph in the preview was extending the selection up to every paragraph above it. Cause: pdfjs v5's TextLayer doesn't ship the `.endOfContent` anchor element older versions used to, so empty-space clicks (margins, gaps between paragraphs) anchored on the first text span in DOM order — i.e. the top of the page. We now create the anchor ourselves and toggle a `.selecting` class on pointerdown so the anchor expands to cover the whole layer during a drag.
All changes in version 2026.05.08-11
- FixedChat: PDF text-layer selection now stays where you started — added the `.endOfContent` anchor element pdfjs v5 doesn't auto-create, plus pointerdown/up listeners that toggle the `.selecting` class to expand the anchor during a drag
v2026.05.08-10
May 8, 2026Chat with PDF — select text and ask about it
PDF preview pages now have a transparent selectable text layer over the rendered canvas — drag-select like you would on any web page. Right-click on a selection and a small popover pops up with a question input. Type your question, hit Enter, and the selected passage flows into the chat thread as a quoted prefix to your question, so the AI can answer about that exact span.
All changes in version 2026.05.08-10
- NewChat: PDF preview pages are now text-selectable — pdfjs `TextLayer` is rendered transparently over the canvas, so drag-select, double-click-to-word, and copy-to-clipboard all work
- NewChat: right-click a selection in the preview to open an "Ask about this passage" popover; submitting sends the selection (as a markdown blockquote prefix) plus your question to the chat thread
v2026.05.08-9
May 8, 2026Chat with PDF — citations highlight the actual text
Citations were flashing a vertical band sized to the chunk's share of the page — better than whole-page, still rough. The preview now matches the chunk's text against pdfjs's extracted text layer and flashes the actual text bbox: the exact paragraph or sentences the AI cited. Falls back to the vertical band if the PDF has no text layer (image-only scans).
All changes in version 2026.05.08-9
- NewChat: citation flashes now land on the actual cited text — preview pane resolves the chunk text against pdfjs `getTextContent` on click and caches the resolved bbox by elementId
- NewChat: new `listChunkTextsForIndex` query returns the full chunk text (no embeddings) for client-side bbox resolution
v2026.05.08-8
May 8, 2026Chat with PDF — block migration actually triggers now
The block-citation migration shipped yesterday relied on `createOrReuseIndex` running on re-upload, but the frontend's `findIndexByHash` shortcut was bypassing it for any non-error cached row — so old PDFs kept getting page-level citations instead of upgrading. `findIndexByHash` now returns null for pre-block-level indexes, forcing the upload to flow through the migration path. Re-upload your PDF and you'll get sub-paragraph citation flashes on the next chat.
All changes in version 2026.05.08-8
- FixedChat: `findIndexByHash` now returns null for pre-block-level indexes (chunks with bare page numbers as elementIds), so re-upload actually triggers the block-upgrade migration instead of joining a stale row
v2026.05.08-7
May 8, 2026Chat with PDF — block-level citations
Citations used to flash the entire cited page when clicked, which was practically useless on dense pages. Each chunk now has its own pdfIndexElement with an approximate vertical-band bbox, so clicking a citation chip flashes the specific paragraph rather than the whole page. The model is told the block id of every chunk in its context (`<!-- block id="3-c-2" page="3" -->`) and cites with `<cite p="3" id="3-c-2"/>`. Existing PDFs auto-upgrade on next re-upload — no double-charge.
All changes in version 2026.05.08-7
- NewChat: citation chips now point at chunks, not pages — clicking flashes the specific paragraph (vertical-band bbox approximation, sized to the chunk's share of the page)
- NewChat: model prompt now teaches the block-id format and instructs `<cite p="N" id="N-c-K"/>` so citations carry sub-page granularity
- NewChat: full-context path (≤50 pages) sends every chunk in chunkIndex order with block markers, so the model can cite the exact paragraph anywhere in the doc
- ImprovedChat: createOrReuseIndex auto-detects pre-block-level indexes (chunks whose elementIds are bare page numbers) and re-runs OCR+chunking to backfill block elements — no extra charge for the migration
v2026.05.08-6
May 8, 2026Chat with PDF — answers render properly, citations actually click
Three rendering bugs collapsed into one bad screenshot: assistant messages were dumping raw markdown (literal `**Helga**`), every bare citation chip was getting a unique number even when it pointed at the same page (so you saw chips numbered 1–42 instead of one per page), and all chips were marked as "missing" so clicking did nothing. Now: messages render with markdown formatting, citation chips dedupe by page, and clicking one scrolls + flashes the cited page in the preview. Also told the model to stop stacking five citations on one claim.
All changes in version 2026.05.08-6
- FixedChat: assistant messages now render markdown (bold, italics, lists, headings) instead of dumping raw `**asterisks**` and unbroken text
- FixedChat: citation chips now dedupe by elementId — bare `<cite p="N"/>` tags are resolved to the page-cover element so two citations of the same page share one numbered chip
- FixedChat: citation chips are now actually clickable — the resolution path was treating bare cites as orphaned, leaving every chip in the disabled / "Citation not found" state
- ImprovedChat: tightened the citation prompt — one citation per claim instead of "cite every page where the info appears", which had the model emitting up to 11 chips next to a single character name
v2026.05.08-5
May 8, 2026Chat with PDF — smarter answers, real citations
Switched the chat model from gpt-4o-mini to Mistral Large for better instruction-following — the model now actually emits the <cite p="N"/> markers the system prompt asks for. For documents up to 50 pages, the entire OCR'd markdown gets stuffed into the prompt instead of vector search, so broad questions like "list all characters" stop missing the long tail. Longer docs use a wider top-20 retrieval (was top-8). Citation chips appear under the answer and clicking one scrolls + flashes the cited page in the preview.
All changes in version 2026.05.08-5
- NewChat: full-document context for short PDFs (≤50 pages) — entire OCR'd markdown is sent to the model, eliminating retrieval misses on broad questions
- NewChat: vector retrieval bumped from top-8 to top-20 for documents longer than 50 pages
- ImprovedChat: switched chat model from gpt-4o-mini (OpenAI) to mistral-large-latest (Mistral). Same key as OCR, no extra setup, stronger instruction-following on the citation rules
- FixedChat: tightened the system prompt — citations are now framed as non-negotiable with concrete examples, so the model stops dropping <cite p="N"/> markers on broad answers
v2026.05.08-4
May 8, 2026Chat with PDF — chat works again, preview stays sharp
Switched the chat backend from a flaky Replicate proxy of DeepSeek-V3 (which was rejecting our payload at the SiliconFlow upstream) to a direct OpenAI gpt-4o-mini call — same citation-grounded answers, faster, and one fewer API key to manage. Fixed the blank-white PDF preview by making the shared page renderer wake consumers up when the doc finishes loading. Reworked the indexing scan animation into a softer two-layer sweep that doesn't fight the underlying text.
All changes in version 2026.05.08-4
- FixedChat: replaced Replicate DeepSeek-V3 with OpenAI gpt-4o-mini for chat completions — Replicate's SiliconFlow upstream was returning "Message field is required" for our OpenAI-style payload
- FixedShared PDF renderer: `renderPage` identity now bumps on internal version change, so consumer effects (PdfPageCanvas) re-fire when the doc finishes loading instead of leaving the canvas blank white
- ImprovedChat: redesigned the indexing scan animation — soft tint pulse + thin gradient line tracer
- ImprovedBackend: dropped REPLICATE_API_TOKEN dependency from the chat path — only MISTRAL_API_KEY (OCR) and OPENAI_API_KEY (embeddings + chat) are needed now
v2026.05.08-3
May 8, 2026Chat with PDF — preview pane polish
Fixed the blank white preview that hit when the page-dimension load raced the canvas renderer — pages now paint as soon as they're ready. Added a soft cyan scanner-pass animation that sweeps each page while the index is being built, so the wait feels intentional. Cleaned up the redundant per-page "Failed" pill that was just repeating the doc-wide status on every card.
All changes in version 2026.05.08-3
- FixedChat: PDF preview no longer renders blank white — page dimensions are now read from the same pdfjs document the canvas paints from, eliminating a race that left the canvas unrendered
- FixedChat: re-uploading a PDF that previously failed to index now actually retries — the cache-hit shortcut was joining errored rows and silently inheriting the old failure
- FixedChat: thread pane shows the actual indexing error instead of a misleading 'Starting up…' message when the index is in an error state
- NewChat: scanner-pass animation overlays each page while the index is being OCR'd or embedded
- ImprovedChat: dropped the per-page status pill from the preview — the doc-wide indexing state already lives in the header, repeating it on every page was noise
v2026.05.08-2
May 8, 2026Chat with PDF — indexing is fast now
Replaced the per-page vision-OCR loop with a single Mistral OCR call. A 50-page PDF now indexes in ~10–20 seconds instead of ~3 minutes. Chat unlocks the moment the first chunks are embedded — you can start asking questions while the rest of the document is still being processed in the background.
All changes in version 2026.05.08-2
- ImprovedChat: switched OCR provider from DeepSeek-OCR (per-page Replicate predictions) to Mistral OCR (single whole-document API call) — kills the ~5s/page latency tax
- NewChat: 'partial' index status — chat input unlocks after the first batch of chunks is embedded; remaining chunks finish in the background
- ImprovedChat: browser no longer renders or uploads page images — only the original PDF is uploaded, Mistral fetches it directly
v2026.05.08
May 8, 2026Chat with PDF — rebuilt around persistent indexing
Reload-safe, regenerate-safe, no re-OCR. Upload a PDF once and we index it in the background — content-hashed, so re-uploads of the same file are instant. The chat is now subscription-aware (free tier with monthly cap) and runs entirely on our infrastructure: server-orchestrated DeepSeek-OCR with proper rate-limit retry, OpenAI text-embedding-3-small for chunked retrieval, DeepSeek-V3 over only the relevant chunks. Grounded citations with click-to-source preserved.
All changes in version 2026.05.08
- ImprovedChat: moved OCR + chat off the browser onto Convex actions — pages are no longer re-indexed on reload, regenerate, or new questions
- NewChat: content-hash dedup so re-uploading the same PDF skips the entire pipeline and reuses the existing index
- NewChat: vector retrieval (top-8 chunks per question) instead of stuffing the full markdown into every prompt — flat input cost regardless of doc length
- NewChat: upload-first /chat UI with PDF preview on the left and chat on the right, replacing the previous chat-input-as-landing flow
- FixedChat: 429 rate-limit hell — Replicate calls now run server-side, sequentially, with Retry-After-aware exponential backoff
- ImprovedChat: removed the BYOK Replicate proxy, the local Gemma fallback, the in-browser bbox cache, and the IndexedDB chat-files layer in favor of a single auth-gated cloud path
v2026.05.07
May 7, 2026Cloud Chat with Click-to-Source Citations
Bring your own Replicate token and the chat sidebar gains a cloud mode powered by DeepSeek-OCR + DeepSeek-V3. OCR indexes every page with bounding-box grounding so answers come with citation chips you can click to jump straight to the highlighted region on the source page. Same conversation, same slash commands — but with cited answers and an extensible provider layer so we can swap in Kimi or Claude later without touching the UI.
All changes in version 2026.05.07
- NewChat: new Cloud AI mode (BYOK Replicate) — DeepSeek-OCR indexes pages with bbox grounding, DeepSeek-V3 chats over the index with native tool-calling into the existing PDF processors
- NewChat: citation chips appear under cloud answers; clicking a chip opens the cited page with the bbox highlighted
- NewCloud AI: abstract `CloudAiProvider` layer with Replicate proxy through Convex — adding new providers (Kimi, Claude direct, OpenAI) is one new file
v2026.05.06
May 6, 2026Workspace Size-Limit Errors Now Tell You What To Do
Drop a file bigger than the free workspace cap and you used to get a dead-end "exceeds 10MB limit" banner. Hard tier blocks now pop an Upgrade to Pro dialog directly, with the cap spelled out and a one-click checkout path.
All changes in version 2026.05.06
- NewWorkspace: hitting the free file-size or file-count cap now opens the Upgrade to Pro dialog directly instead of showing a passive banner
- FixedWorkspace: size-limit error no longer prints the filename twice and no longer fires an infra-failure analytics event for what is really a tier limit
v2026.05.05
May 5, 2026Tools Open Cleanly From Any Entry Point
Edit PDF, Compare, Compress, Redact, Remove Watermark, the PDF→Text/Word/Markdown family, and several shared rendering paths could all throw "No GlobalWorkerOptions.workerSrc specified" if you opened them as your first tool in a session. Every pdfjs entry point now configures its own worker instead of relying on a different code path to do it first.
All changes in version 2026.05.05
- FixedPDF tools: "No GlobalWorkerOptions.workerSrc specified" error on direct tool entry across the fleet
v2026.05.03
May 3, 2026Two New Alternative Guides on the Blog
Deep-dive comparisons for people searching "Sejda alternative" and "Wondershare PDFelement alternative" — what each product actually is in 2026, what their free tiers really do, and where their privacy stories hold up or fall apart.
All changes in version 2026.05.03
- NewBlog: Sejda PDF Editor alternative — pricing, the 3-task wall, watermarks on Edit and Sign
- NewBlog: Wondershare PDFelement alternative — the cloud AI behind the desktop UI, the 2025 RepairIt CVE incident, and the trial that watermarks everything
v2026.04.30
April 30, 2026Crisp PDF Pages at Any Zoom
Edit PDF and PDF Reader now paint pages straight onto a canvas sized to your actual device pixels, so text stays sharp on Retina and at high zoom instead of going through a PNG that the browser had to smooth out.
All changes in version 2026.04.30
- FixedEdit PDF: text no longer looks blurry on Retina or at high zoom
- FixedPDF Reader: render scale now tracks devicePixelRatio × zoom, and the pdfjs document is loaded once per file instead of on every page or zoom change
- FixedEdit PDF: navigating between pages no longer flashes a tiny stretched thumbnail — adjacent pages are pre-rendered and the fallback uses a sharper 400px thumbnail
- ImprovedNew shared PdfPageCanvas component and usePdfPageRenderer hook so every tool that displays a PDF page renders it crisply by default
v2026.04.17
April 17, 2026Unified Action Bar Across Every Tool
Every PDF tool now shares the same action bar — workspace, mobile, and desktop. Big accent-colored primary button with a ⌘↵ shortcut, plus a Start over button with ⌘⌫ on standalone pages.
All changes in version 2026.04.17
- ImprovedEvery tool now uses the same ToolWorkspaceActionBar in the workspace and on its standalone page
- NewCmd/Ctrl+Enter fires the primary action in every tool
- NewCmd/Ctrl+Backspace triggers Start over on standalone tool pages
- ImprovedDropped the one-off inline action bars from compress, OCR, split, sign, and remove-watermark
- ImprovedSVG to PDF preview restyled to match the shared action bar pattern
- ImprovedBatch file tabs now follow the tool color — sticky count pill and accent underline on the active tab
v2026.04.15
April 15, 2026Intelligent Text Extraction
New Rust→WASM engine for smart PDF text extraction with layout analysis. PDF to Word, PDF to Markdown, and PDF to Text now produce significantly better output with proper headings, lists, and structure.
Rich Tool Pages
Every tool page now features in-depth articles, FAQs with structured data, and visual diagrams — making it easier to understand each tool and boosting discoverability.
All changes in version 2026.04.15
- NewRust→WASM PDF text extraction engine with intelligent layout analysis and content classification
- NewPDF to Word now uses markdown-aware extraction for better structure preservation
- NewPDF to Markdown and PDF to Text upgraded with the new extraction engine
- NewArticle sections and FAQ sections with structured data (SEO) on all tool pages
- NewCollapsible article and FAQ sections for cleaner tool page layout
- NewVisual SVG diagrams across 15+ tool pages — Merge, Split, Sign, Protect, Edit, and more
- NewNew blog articles: Convert PDF to JPG, Create Fillable PDF, Best Free PDF Reader, PDF to Text
- NewBlog link added to header and footer navigation
- NewLicense key management for purchasers via Polar integration
- ImprovedHome page copywriting refreshed for clarity and conciseness
- ImprovedBlog cover images converted to WebP for faster loading
- ImprovedTool conversion card simplified — removed Pro upsell for authenticated users
- FixedSEO improvements — robots.txt rules, canonical links, and updated sitemaps
v2026.04.13
April 13, 2026Split Scanned Pages
New tool for splitting two-up scanned pages into individual pages. Handles booklets, side-by-side scans, and A3-to-A4 splitting with guided configuration.
Advanced PDF Compression
Compress PDF now handles huge files (up to ~1 GB). Set a target file size and let the tool auto-optimize, convert to grayscale for line drawings, or use the new Extreme preset.
All changes in version 2026.04.13
- NewSplit Scanned Pages tool for separating two-up scans into individual pages
- NewTarget file size mode — specify a size limit and auto-find optimal compression settings
- NewGrayscale option for massive savings on architectural drawings and line art
- NewExtreme compression preset (72 DPI, 35% quality) for screen-only viewing
- NewLoading progress bar with real-time feedback when opening large PDFs
- NewLarge file upload nudge — guides free users toward Pro when processing big files
- NewContextual upgrade prompts within tool pages for free users
- ImprovedPDF processing switched from ArrayBuffer to blob URLs for better memory management across all tools
v2026.04.10
April 10, 2026Cleaner Navigation
Streamlined top navigation bar — secondary links collapsed into a compact "More" menu for a cleaner, less cluttered header.
All changes in version 2026.04.10
- ImprovedTop navigation decluttered — Download, Changelog, and Feedback collapsed into a "More" dropdown
- ImprovedHeader internals reorganized into smaller extracted components
- NewAnnouncement banner now has a dismiss button (persists per session)
v2026.04.07
April 7, 2026Chat (Beta)
Ask questions about your PDFs using on-device AI. Upload a file and chat — all processing stays local. Slash commands coming soon.
All changes in version 2026.04.07
- NewChat feature now available to all users (Beta) — no feature flag required
- NewAsk AI questions about PDF content with on-device vision models
- ImprovedRenamed "Chat PDF" to "Chat" across navigation and UI
- ImprovedSlash commands temporarily disabled — coming soon
v2026.04.03
April 3, 2026Typography Refresh
New font system with DM Sans, Fraunces, and IBM Plex Mono for sharper, more consistent typography across all platforms.
Desktop Auto-Updates
The desktop app now checks for and installs updates automatically with a splash screen and status notifications.
All changes in version 2026.04.03
- NewAuto-update with IPC communication, status notifications, and install prompts
- NewSplash screen with theme-aware styling and fade-out transition
- ImprovedFont families updated to DM Sans, Fraunces, and IBM Plex Mono across all apps
- ImprovedResponsive layout improvements for file dropzone, tool cards, and announcement banner
- ImprovedGrid structure and alignment overhaul for tool pages across devices
- ImprovedRotate PDF layout standardized with consistent padding and spacing
- ImprovedMerge PDF layout and styling enhanced for mobile and desktop
- ImprovedReplaced 'embedPages' with 'embedPdf' method across background color, fix page size, and booklet tools
- ImprovedDynamic gradient IDs using useId hook to prevent SVG conflicts
- NewRevamped download instructions with detailed macOS installation guide
- ImprovedTool upload card badge positioning and flex properties improved
- ImprovedPadding and spacing standardized across all tool components
v2026.03.31
March 31, 2026Electron Desktop
Rebuilt desktop app on Electron with native tabbed editing, platform detection, and improved build pipelines.
License & Accounts
License validation API, account-based activation, and rate-limited validation endpoints for Pro users.
Tools Browse & Pinning
Pin your favorite tools, filter by category, and browse the full toolkit from a redesigned tools page.
All changes in version 2026.03.31
- NewElectron desktop app with native window controls and platform detection
- NewTab bar with drag-and-drop reordering, search params, and window controls
- NewLicense validation API with rate limiting (5 per minute per IP)
- NewAccount-based activation and sign-in flow
- ImprovedStreamlined license validation logic and error handling
- NewTool pinning in the browse page with persistent favorites
- ImprovedEnhanced tool filtering with additional category support
- ImprovedPricing details, descriptions, and comparison metrics updated
- NewEarly bird spots API for tracking claimed spots
- ImprovedEarly bird data migrated from hardcoded values to API integration
- ImprovedDownload manifest simplified with static asset mapping
- FixedPDF compatibility utilities for older browsers
- FixedUndefined page sizes and positions handled across multiple tools
v2026.03.26
March 26, 2026Edit PDF
Full annotation toolkit — add text, drawings, stamps, symbols, callouts, and freehand highlights with undo/redo and draft auto-save.
Desktop App
OxygenPDF now runs natively on macOS, Windows, and Linux with tabbed editing, keyboard shortcuts, and drag-and-drop tab reordering.
Workflow Builder
Chain multiple PDF operations into reusable workflows — build once, run on any file.
All changes in version 2026.03.26
- NewEdit PDF tool with text, image, and drawing annotations
- NewStamp and symbol presets with drag-and-drop placement
- NewCallout annotations with click-to-place and drag-to-place
- NewFreehand highlighter with customizable stroke width and dash patterns
- NewUnified undo/redo history for annotations and page operations
- NewDraft auto-save and restore for in-progress annotations
- NewFit-to-page, fit-to-width zoom, and zoom presets
- NewDynamic floating panels for pages and layers with resizable widths
- NewNative desktop app for macOS, Windows, and Linux
- NewTabbed editing with drag-and-drop reordering and close guards
- NewGlobal keyboard shortcuts for undo, redo, save, and load
- NewDirty tab tracking with unsaved-changes indicators
- NewWorkflow builder with visual tool chaining and save/load
- NewFree workflow save limits with upgrade prompts for Pro
- NewOCR PDF tool with Tesseract, Florence-2, and SmolVLM engines
- NewOCR progress display with live preview, cancellation, and stuck detection
- NewOCR history tracking and engine selection across tools
- NewAI OCR support in PDF to Markdown, PDF to Text, and PDF to Word
- NewInsert Watermark tool with text, image, and pattern modes
- NewRemove Watermark tool with detection and before/after preview
- NewCreate PDF tool with blank, template, and HTML modes
- NewExpandable template gallery with state management
- NewPDF to Form tool for converting fillable PDFs into web forms
- NewPDF Merge interleave and interleave-reverse modes with blank page option
- NewPage range support for delete, extract, and split operations
- NewCommand palette for quick navigation and tool search
- NewAccount-based activation and sign-in with OxygenPDF accounts
- NewPricing page and early bird spots tracking
- NewFeedback dialog for collecting user input
- NewInline file renaming in the workspace
- NewFloating action bars and bottom sheet settings across all tools on mobile
- ImprovedStandardized password-protected PDF decryption across all tools
- ImprovedTerminology updated from "upload" to "select" for privacy clarity
- FixedFile input cleanup to prevent stale hidden inputs after selection or cancellation