Skip to main content

Overview

Experimental, and no longer linked from the editor. The editor’s “Open in immersive mode” button was removed with the floating preview controls, so there is no way to reach immersive mode from the editor’s screens today. It still works where a site mounts the widget itself, which is what the rest of this page describes. It is for developers; nothing here is something an editor can switch on.
Immersive mode renders the editing UI directly on the site page — no iframe, no chrome around the content. The widget (chat FAB, add-block pill, text-selection “Ask AI”, inline field prompts, undo/redo) is portalled into the site DOM. It is the intended mode for copy-heavy and single-page edits where you want to see the real page at full width. The main editor (apps/editor) and immersive mode share the same draft via the orchestrator’s (session, siteId, slug) — switching between them does not move or duplicate state.

Entering immersive mode

On a site that renders the widget on its draft route, as this repository’s demo site does, immersive mode is a URL:
A site that guards its draft route with a draft secret also needs that secret on the URL, in plain view, so treat such a link as a credential. The editorOrigin query param tells the widget where the main editor lives so it can render the “Back” pill. The site’s __editor=1 middleware rewrites the request to the preview-draft route internally; you do not link to /preview-draft/... directly. Navigation inside the page keeps the immersive parameters.

Returning to the main editor

A small glass ”← Back” pill sits in the top-left corner of the immersive page. It:
  • only renders when config.editorOrigin is present (so embedded / public widget uses stay clean),
  • auto-hides when the chat panel is open (to stop it colliding with panel chrome),
  • auto-hides on scroll-down and reappears on scroll-up or near the top (browser-chrome style),
  • navigates same-tab to {editorOrigin}?session=...&siteId=...&slug=....
The editor reads ?slug= on bootstrap, so you land on the exact page you were editing — no flash to the home route before the iframe catches up.

What is available inside immersive mode

  • Chat FAB (bottom right) with the full chat pipeline — text, structural, and image ops all route through the same orchestrator as the main editor.
  • Add-block pill (bottom right, next to the chat FAB) opens the inline block picker above itself and inserts after the last block on the page.
  • Inline field prompt — click any editable text field to get a tight prompt box anchored to the field.
  • Text selection “Ask AI” — select a range of copy to get an inline toolbar. In text-only mode, selections route into the field prompt with the excerpt pre-filled.
  • Undo / redo — Cmd+Z and Cmd+Shift+Z / Cmd+Y. Buttons also appear in the chat panel header. Proxies to the orchestrator’s /history/{undo,redo} so draft history is shared with the main editor.

Feature flags

Key files


Embedding on an external site

The widget lives in packages/immersive-widget and can be mounted on any Next.js 15 or 16 (App Router) site already integrated with the orchestrator via the site-sdk. The monorepo’s apps/site is its first consumer.
@ai-site-editor/immersive-widget is not published to npm — it is private: true, has no build step, and its entry points are raw .ts/.tsx source. pnpm add will 404. To use it today, vendor the package into your repo or consume it from a checkout of this monorepo, and add it to transpilePackages in your next.config (apps/site does the same). The snippets below assume that wiring.

Prerequisites

  • Next.js 15 or 16, App Router (React 19 peer dep)
  • Site already registered with the orchestrator (npx avocado-register) — you need a valid session and siteId
  • The orchestrator reachable at a known URL from the browser (it’s called directly by the widget over SSE, not proxied through Next.js)

Install

Import the CSS

Add the widget stylesheet once, in your root layout or a global CSS file:
The styles use .iw-* class names with a --iw-* CSS custom property API so they don’t conflict with your site’s own styles.

Mount the widget

Create a "use client" wrapper — the widget needs browser APIs and the Next.js router:
Then render it from your page component (must be a Server Component that passes the slug down):
The widget portals itself to document.body, so its position in the tree doesn’t matter for z-index or layout. Without a manifest the widget’s block picker falls back to the SDK’s built-in block list. To show only your site’s actual blocks, fetch the manifest from your existing /api/editor/blocks route and pass it in:
Update the client wrapper’s props signature to accept and forward manifest.

Session management

WidgetConfig.session is the orchestrator session key that scopes the draft. You have a few options: The main editor passes ?session=... in the URL when opening immersive mode — read it via useSearchParams() if you want the immersive view to share the editor’s session automatically.

Props reference

Restricting access

The widget renders unconditionally if mounted. Gate it behind your existing auth check before rendering the component — for example, only mount it when a ?__editor=1 query param is present or when a Draft Mode cookie is set:

Visual editing with Puck

The other direct-manipulation surface — drag and drop on the same blocks, built on Puck (puckeditor.com).

A tour of the editor

The chat editor these modes sit alongside.