Skip to content

Rewrite site copy from a plain-language instruction - #379

Merged
singhharsh1708 merged 2 commits into
mainfrom
feat/ai-editing
Sep 6, 2026
Merged

Rewrite site copy from a plain-language instruction#379
singhharsh1708 merged 2 commits into
mainfrom
feat/ai-editing

Conversation

@singhharsh1708

Copy link
Copy Markdown
Owner

Describe the change you want in plain language and the editor rewrites the section copy from it. One instruction, one undo step.

The v0.4.0 teardown removed chat editing because it was an AI feature attached to a product that had no keys, no accounts and nothing to configure. This is narrower on purpose, so that stays true: an instance without a key does not show the button, and a fresh clone still runs on an empty environment.

What it does

  • A Rewrite button in the editor toolbar opens a bar: an instruction field, three starting suggestions, a character counter and a note saying what is sent where.
  • The result goes through commitSections, so it lands as a single undo step. The toast says so.
  • It rewrites eyebrow, heading, body and ctaLabel. Nothing else. Layout, colours, images, button links, scroll heights and visibility are read back from the document, not from the reply, so a model that redirects a call to action or stretches a scroll track changes nothing.
  • Spacers are returned untouched.

Not trusting the reply

The reply is copy, not a document. In order:

  1. The completion is located inside whatever prose or code fence the model wrapped it in.
  2. It is parsed as JSON, then validated against sectionsSchema — the same schema the editor's own document has to satisfy.
  3. The section count must match. A reply that drops or adds one is rejected whole.
  4. Only the four copy fields are read out of it. Everything else comes from the original.
  5. The editor validates the response a second time before applying it, because a proxy, an extension or a stale deploy can sit between the route and the browser.

Anything that fails returns a 422 and the document is left alone.

Configuration

Variable Effect
SARVAM_API_KEY Turns the feature on. Absent, GET /api/edit-site answers {"available":false} and the editor renders no button
SARVAM_MODEL sarvam-105b (default) or sarvam-105b-conversations
SARVAM_BASE_URL Any endpoint speaking the OpenAI chat-completions shape. Defaults to Sarvam. Restricted to http(s)

Rate limited at 10 per five minutes per IP, tighter than export's 30 per minute: an export costs CPU, a rewrite costs credit that runs out for everyone at once. Body capped at 400 KB with the same running-cap reader export-site uses, instruction capped at 600 characters, and at most 40 sections per call. maxDuration = 60 with a 50s abort, so a slow completion returns this route's own error rather than a gateway timeout.

Verified

42 new tests, suite 388 to 432, all passing.

Five guards mutated one at a time to prove the tests bite:

Mutation Result
return the model's reply instead of merging copy only 2 failed
skip sectionsSchema validation of the reply 2 failed
serve the route with no key configured 3 failed
drop the section-count check 2 failed
drop the instruction length cap 1 failed

End to end in headless Chrome against the production build, with a local endpoint standing in for Sarvam (SARVAM_BASE_URL), driving the real UI:

  • aurabeauty — 9 sections sent, all 7 non-spacer headings rewritten, spacers untouched, toast shown, Undo restored the original list exactly.
  • ledger-fintech — same, on a second template.
  • Request received by the stub carried Authorization: Bearer <key> and model=sarvam-105b; the reply came back inside a ```json fence and parsed.
  • Round trip through the route preserved ctaHref: "#section-9" while changing the heading.
  • Unconfigured instance: {"available":false}, POST 503, and no button in the DOM.

Screenshotted at 1440x900 and 390x844: no horizontal overflow at either width, and the submit button is hit-testable via elementFromPoint once enabled.

tsc --noEmit clean, eslint clean, production build clean.

Not verified: the live Sarvam API. There is no key in this environment, so every assertion above comes from mocked fetch or the local stub. The request shape is built from the published contract at docs.sarvam.ai/api-reference/chat/chat-completionsPOST /v1/chat/completions, bearer auth, OpenAI-shaped response — but nobody has run it against api.sarvam.ai. To try it: put a key in .env as SARVAM_API_KEY and run npm run dev.

Also

  • The privacy page said two things leave your browser. It now says three, names the rewrite request as the one that only happens if you ask, lists Sarvam under third-party services, and drops the flat "nothing you type reaches us" that this makes untrue.
  • contactDetails.test.ts had a test called "promises no AI, which the product does not have". That premise is no longer true, so it is now two tests that check what still matters: the landing, presets, create and about pages must not promise a feature they cannot know is configured, none of them may use the marketing register v0.4.0 removed, and the README must mention the key in the same breath as the rewriting.
  • Changelog entry v1.1.0.

Limits

It rewrites; it does not restructure. Adding, removing or reordering sections stays manual, because section count drives the scroll track and a model that quietly returns eight sections for nine would silently retime the page.

@vercel

vercel Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
scrollcraft Ready Ready Preview Sep 6, 2026 7:33pm UTC

@singhharsh1708
singhharsh1708 merged commit 8121790 into main Sep 6, 2026
3 checks passed
@singhharsh1708
singhharsh1708 deleted the feat/ai-editing branch September 6, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant