html-docs · engineering changelog

Beautify & Docsmith — what changed

Seven changes across the AI document features: pluggable model providers, a latency fix, a live‑streaming preview, and design‑system + streaming upgrades to the Docsmith chat.

Branch main · 7 commits · Jun 2026

TL;DR

The headline changes

feature

Model dropdown in Beautify

Pick the inference engine: OpenAI (default), Claude (Anthropic Messages API), or OpenInfer (@oi/beta). One source of truth in lib/beautify-providers.ts.

feature

OpenInfer in Docsmith chat

The chat model list gained @oi/beta, routed through an OpenAI‑compatible client pointed at OpenInfer's Responses API.

fix

Apply is instant again

Applying a preview re‑ran the whole model. Now it reuses the previewed HTML — Apply is a DB write, not a second generation.

feature

Live‑streaming preview

Beautify now constructs the preview on screen as the model writes it, with a “Constructing…” spinner — instead of one long blank wait.

feature

Docsmith knows the design system

The chat now carries the same DESIGN_SYSTEM Beautify uses, and can insert div/table blocks for diagrams & tables.

fix

Docsmith streams again

Added no‑buffer headers so edge proxies stop holding the chat’s SSE stream and dumping it all at once.

01 · Pluggable providers

How a Beautify request reaches a model

A new Model dropdown sits atop the Beautify dialog. The selection flows through the server action into a single dispatcher that fans out to the chosen provider. OpenAI stays the default, so nothing breaks for existing users.

Beautify dialogModel ▾
Server actionpreviewBeautify ·
beautifyDocument
Prompt buildbuildBeautifyPrompt
+ DESIGN_SYSTEM
DispatchercallBeautifyModel(provider)
OpenAI · default
Chat Completions · OPENAI_API_KEY
Claude · Anthropic
Messages API (SDK, streaming) · ANTHROPIC_API_KEY · claude-opus-4-8
OpenInfer
Responses API (SSE) · OPENINFER_API_KEY · @oi/beta
OpenAIClaude / AnthropicOpenInfer
Fig. 1 — One dispatcher, three engines. Each provider only needs its own API key; an unset key just disables that one option.

02 · Fix — the “Applying…” hang

Apply was generating the document twice

After previewing, clicking Apply to Document called the model all over again (and re‑compiled Tailwind), discarding the HTML the preview had just produced. With a slow or streaming provider, that second pass made “Applying…” appear stuck.

Before
Preview → full model generation ~20–90s
Applyfull model generation again ~20–90s
▸ result: duplicate cost, “Applying…” hangs
After
Preview → model generation, HTML kept
Applyreuse the previewed HTML DB write
▸ result: Apply is near‑instant; falls back to regenerate only if no preview HTML is supplied

The previewed HTML now rides through onTransform → beautifyDocument(…, precomputedHtml), which persists it directly. The rollback‑version snapshot still runs, so Version History is unchanged.

03 · Live‑streaming preview

Watch the document build itself

The old preview was a single blocking call — a server action awaited the entire generation before returning, so you stared at a spinner the whole time. A new streaming route emits tokens as the model writes them, and the dialog renders the partial HTML live. Because the model emits the <style> in the <head> first, every element that follows streams in already‑styled.

Dialoglive iframe /api/beautify/previewSSE route streamBeautifyModelprovider stream Model POST {doc, template, provider} event: delta → partial HTML painted live (~4×/s) event: done → finalized HTML (images restored, Tailwind compiled) → enables Apply
Fig. 2 — Tokens stream Model → route → dialog. The final done event carries the exact HTML that Apply persists, so accepting stays instant.

lib/beautify-core.ts

New plain module (a route can’t import a 'use server' file). Holds the prompt builder, per‑provider streaming generators, finalize, and shared helpers. Design system stays single‑sourced via DESIGN_SYSTEM.

/api/beautify/preview

SSE route: streams delta chunks, then a done event with the finalized document.

beautify‑document‑dialog

Consumes the stream into a live iframe with a “Constructing…” spinner; Apply is gated until done; Cancel aborts.

Purely additive

The original previewBeautify server action and the Apply path are untouched, so the non‑streaming flow remains as a fallback.

04 · Docsmith chat

Design‑system awareness, richer inserts, and real streaming

feature

Knows the house style

The chat system prompt now injects the same DESIGN_SYSTEM Beautify uses — with a block‑level adapter note (inline styles, build diagrams as inline <svg> / styled boxes). It no longer asks you to “share your design principles.”

feature

Diagram & table inserts

Insertable block tags widened to include div and table (schema + ALLOWED_INSERT_TAGS), so it can add styled containers and tables instead of coercing them to <p>.

fix

Streams again

The chat SSE response now sends Cache‑Control: no‑transform and X‑Accel‑Buffering: no, matching the beautify route, so proxies stop buffering the whole reply.

Why it looked non‑streaming

The chat already streamed at the SDK layer, but the response carried no buffer‑control headers. Some edge proxies hold an SSE body until it closes and then deliver it in one shot — so the reply appeared all at once. The new headers tell the proxy to pass bytes straight through. If it still feels delayed on the default reasoning model, that’s the model thinking silently before its first visible token — a separate, opt‑in “show reasoning” improvement.

05 · Commit timeline

What landed, in order

Model/provider selector for Beautify
6ad775b · beautify‑providers.ts · documents.ts · dialog
OpenAI + Claude + OpenInfer behind a dropdown.
OpenInfer option in Docsmith chat
a7b1089 · api/chat/route.ts · chat‑panel.tsx
OpenAI‑compatible client at OpenInfer’s base URL.
Fix: Apply re‑ran the model
96fbd59 · reuse previewed HTML
Apply becomes a DB write.
Docsmith gets the design system + div/table inserts
a95ab2b · route.ts · chat.ts
House‑style awareness; richer block inserts.
Live‑streaming Beautify preview
7f65e7c · beautify‑core.ts · /api/beautify/preview · dialog
Preview constructs on screen.
Fix: Docsmith stream buffering
ed1eadb · api/chat/route.ts
No‑buffer SSE headers.

06 · Where things live

File map

PathRole
lib/beautify-providers.tsProvider registry (id / label / default) — shared by dialog & server.
lib/beautify-core.tsNew shared engine: prompt build, per‑provider streaming + accumulate, finalize, helpers.
lib/actions/documents.tspreviewBeautify / beautifyDocument (+ precomputedHtml fast path); non‑streaming provider dispatch (fallback).
app/api/beautify/preview/route.tsStreaming SSE preview endpoint.
components/beautify-document-dialog.tsxModel dropdown + live streaming preview UI.
app/api/chat/route.tsDocsmith: OpenInfer routing, design‑system prompt, div/table tags, no‑buffer headers.
lib/actions/chat.tsALLOWED_INSERT_TAGS widened (div, table).
components/chat/chat-panel.tsxOpenInfer in the model list.
.env.exampleDocuments OPENAI_/ANTHROPIC_/OPENINFER_API_KEY.
Config reminder

Keys are server env vars (.env.local locally, Vercel project settings in prod) — not stored in Supabase. Each provider reads only its own key.

html-docs · Beautify & Docsmith changelog · generated for review