Add MindSpace visual page editor and Plaza local Tunnel deployment.

In-preview HTML editing with undo/redo sync replaces Agent patch auto-save; Plaza runs on local Mac via Cloudflare Tunnel with embed height and CSP fixes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
John
2026-06-15 22:57:22 -07:00
parent 6ee6fd64dd
commit 25d9c8c364
24 changed files with 1173 additions and 351 deletions
@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import { fetchMindSpacePageDraftPreview } from '../api/client';
import { bindIframeDocumentHeightSync } from '../utils/iframeContentHeight';
import { applyPageChangeHighlight } from '../utils/mindspacePageChangeHighlight';
export type MindSpacePreviewChangeHighlight = {
@@ -15,8 +16,9 @@ export function MindSpacePageDraftPreviewFrame({
content,
templateId,
reloadKey = 0,
liveUpdate = true,
minHeight = 680,
maxHeight = 4800,
maxHeight = 12000,
className = 'mindspace-page-preview-frame',
changeHighlight = null,
}: {
@@ -26,6 +28,7 @@ export function MindSpacePageDraftPreviewFrame({
content: string;
templateId: string;
reloadKey?: number;
liveUpdate?: boolean;
minHeight?: number;
maxHeight?: number;
className?: string;
@@ -62,7 +65,9 @@ export function MindSpacePageDraftPreviewFrame({
}, hasRenderedRef.current ? 450 : 120);
return () => window.clearTimeout(timer);
}, [pageId, title, summary, content, templateId, reloadKey]);
}, liveUpdate
? [pageId, title, summary, content, templateId, reloadKey]
: [pageId, reloadKey, templateId]);
useEffect(() => {
setHeight(minHeight);
@@ -72,27 +77,16 @@ export function MindSpacePageDraftPreviewFrame({
const iframe = iframeRef.current;
if (!iframe || !srcdoc) return;
const syncHeight = () => {
try {
const doc = iframe.contentDocument;
if (!doc) return;
const body = doc.body;
const root = doc.documentElement;
const next = Math.ceil(
Math.max(
body?.scrollHeight ?? 0,
body?.offsetHeight ?? 0,
root?.scrollHeight ?? 0,
root?.offsetHeight ?? 0,
minHeight,
),
);
setHeight(Math.min(next, maxHeight));
const applyHeight = (nextHeight: number) => {
setHeight(nextHeight);
if (
changeHighlight &&
appliedHighlightRevisionRef.current !== changeHighlight.revision
) {
if (
changeHighlight &&
appliedHighlightRevisionRef.current !== changeHighlight.revision
) {
try {
const doc = iframe.contentDocument;
if (!doc) return;
appliedHighlightRevisionRef.current = changeHighlight.revision;
applyPageChangeHighlight(doc, {
previousTitle: changeHighlight.previousTitle,
@@ -100,14 +94,17 @@ export function MindSpacePageDraftPreviewFrame({
previousContent: changeHighlight.previousContent,
nextContent: content,
});
} catch {
// ignore highlight errors in sandboxed preview
}
} catch {
setHeight(minHeight);
}
};
iframe.addEventListener('load', syncHeight);
return () => iframe.removeEventListener('load', syncHeight);
return bindIframeDocumentHeightSync(iframe, {
minHeight,
maxHeight,
onHeight: applyHeight,
});
}, [changeHighlight, content, maxHeight, minHeight, srcdoc, title]);
if (error) {