@agent-native/core 0.90.3 → 0.90.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agent-native.js +25 -3
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +17 -0
- package/corpus/core/bin/agent-native.js +25 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +18 -18
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +8 -2
- package/corpus/core/src/agent/engine/anthropic-engine.ts +11 -1
- package/corpus/core/src/agent/engine/index.ts +1 -0
- package/corpus/core/src/agent/engine/registry.ts +63 -2
- package/corpus/core/src/agent/production-agent.ts +22 -11
- package/corpus/core/src/agent/run-manager.ts +17 -6
- package/corpus/core/src/agent/run-store.ts +16 -11
- package/corpus/core/src/cli/create.ts +35 -0
- package/corpus/core/src/cli/design-connect.ts +263 -1
- package/corpus/core/src/cli/index.ts +84 -7
- package/corpus/core/src/cli/workspacify.ts +34 -0
- package/corpus/core/src/client/KeepTabOpenNotice.tsx +20 -15
- package/corpus/core/src/client/RunStuckBanner.tsx +14 -13
- package/corpus/core/src/client/agent-chat-adapter.ts +28 -17
- package/corpus/core/src/client/history/VersionHistoryPanel.tsx +77 -47
- package/corpus/core/src/client/history/index.ts +3 -0
- package/corpus/core/src/client/history/use-history.ts +28 -0
- package/corpus/core/src/client/index.ts +8 -0
- package/corpus/core/src/client/review/index.ts +5 -0
- package/corpus/core/src/client/review/use-review.ts +40 -2
- package/corpus/core/src/client/use-run-stuck-detection.ts +8 -5
- package/corpus/core/src/history/actions/create-resource-version.ts +11 -8
- package/corpus/core/src/history/index.ts +0 -1
- package/corpus/core/src/history/registry.ts +24 -12
- package/corpus/core/src/history/store.ts +84 -53
- package/corpus/core/src/review/actions/consume-review-feedback.ts +15 -5
- package/corpus/core/src/review/actions/delete-review-comment.ts +15 -3
- package/corpus/core/src/review/actions/resolve-review-thread.ts +12 -5
- package/corpus/core/src/review/index.ts +0 -5
- package/corpus/core/src/review/registry.ts +24 -12
- package/corpus/core/src/review/store.ts +10 -7
- package/corpus/core/src/server/agent-chat-plugin.ts +6 -0
- package/corpus/core/src/server/credential-provider.ts +13 -3
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +11 -11
- package/corpus/templates/assets/actions/generate-image.ts +64 -28
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +91 -74
- package/corpus/templates/assets/server/lib/generation.ts +29 -4
- package/corpus/templates/assets/server/lib/image-processing.ts +131 -0
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +24 -2
- package/corpus/templates/calendar/AGENTS.md +3 -0
- package/corpus/templates/calendar/actions/create-event.ts +8 -34
- package/corpus/templates/calendar/actions/event-action-helpers.ts +52 -0
- package/corpus/templates/calendar/actions/manage-event-draft.ts +7 -34
- package/corpus/templates/calendar/actions/update-event.ts +10 -23
- package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +47 -0
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +23 -0
- package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +59 -2
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +40 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +34 -1
- package/corpus/templates/calendar/app/hooks/use-events.ts +6 -0
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/calendar/app/i18n-data.ts +40 -0
- package/corpus/templates/calendar/changelog/2026-07-07-you-can-mark-event-guests-as-optional-while-inviting-them-or.md +6 -0
- package/corpus/templates/calendar/server/handlers/events.ts +1 -0
- package/corpus/templates/calendar/server/lib/google-calendar.ts +4 -0
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/calendar/shared/api.ts +2 -0
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +29 -3
- package/corpus/templates/clips/actions/generate-workflow.ts +19 -8
- package/corpus/templates/clips/actions/get-clips-ai-prefs.ts +36 -0
- package/corpus/templates/clips/actions/lib/clips-ai-prefs.ts +28 -0
- package/corpus/templates/clips/actions/regenerate-chapters.ts +21 -7
- package/corpus/templates/clips/actions/regenerate-summary.ts +26 -8
- package/corpus/templates/clips/actions/regenerate-title.ts +44 -7
- package/corpus/templates/clips/actions/update-clips-ai-prefs.ts +50 -0
- package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +7 -2
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +25 -10
- package/corpus/templates/clips/app/i18n/ar-SA.ts +28 -6
- package/corpus/templates/clips/app/i18n/de-DE.ts +28 -6
- package/corpus/templates/clips/app/i18n/en-US.ts +29 -6
- package/corpus/templates/clips/app/i18n/es-ES.ts +29 -6
- package/corpus/templates/clips/app/i18n/fr-FR.ts +30 -6
- package/corpus/templates/clips/app/i18n/hi-IN.ts +26 -6
- package/corpus/templates/clips/app/i18n/ja-JP.ts +27 -6
- package/corpus/templates/clips/app/i18n/ko-KR.ts +28 -6
- package/corpus/templates/clips/app/i18n/pt-BR.ts +28 -6
- package/corpus/templates/clips/app/i18n/zh-CN.ts +26 -6
- package/corpus/templates/clips/app/i18n/zh-TW.ts +25 -6
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +73 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -2
- package/corpus/templates/clips/changelog/2026-07-07-ai-tools-can-include-the-full-video-not-just-the-transcript-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-clips-desktop-now-surfaces-meetings-and-dictate-directly-in-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-meeting-notes-now-explain-exactly-how-to-start-granola-style.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-meeting-start-alerts-now-include-separate-start-notes-and-jo.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-recordings-start-more-predictably-and-newly-saved-clips-refr.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/desktop/src/app.tsx +435 -105
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +27 -21
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -22
- package/corpus/templates/clips/desktop/src/styles.css +167 -2
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -7
- package/corpus/templates/clips/desktop/src-tauri/src/tray_meetings.rs +5 -4
- package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.put.ts +16 -8
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +81 -0
- package/corpus/templates/content/AGENTS.md +1 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +73 -8
- package/corpus/templates/content/actions/_database-utils.ts +2 -1
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +20 -1
- package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +663 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +33 -7
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +53 -4
- package/corpus/templates/content/app/components/editor/database/settings.tsx +24 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +70 -1
- package/corpus/templates/content/app/hooks/use-document-properties.ts +63 -5
- package/corpus/templates/content/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/content/app/i18n-data.ts +31 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-rows-can-be-staged-in-bulk-with-a-reviewable.md +6 -0
- package/corpus/templates/content/changelog/2026-07-07-builder-backed-database-fields-now-edit-like-normal-content-fields.md +6 -0
- package/corpus/templates/content/parity/matrix.md +24 -24
- package/corpus/templates/content/parity/matrix.ts +2 -0
- package/corpus/templates/content/shared/api.ts +59 -4
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +16 -1
- package/corpus/templates/design/actions/add-localhost-screens.ts +1 -0
- package/corpus/templates/design/actions/connect-localhost.ts +26 -3
- package/corpus/templates/design/actions/open-visual-edit.ts +4 -37
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +362 -12
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +89 -14
- package/corpus/templates/design/app/components/design/bridge-security.ts +7 -1
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +1 -1
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +41 -0
- package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-keeps-bridge-access-when-opening-previousl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-now-keeps-the-live-app-connection-authenti.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-uses-live-app-iframes-for-click-to-edit-pr.md +6 -0
- package/dist/agent/durable-background.d.ts +12 -12
- package/dist/agent/durable-background.js +18 -18
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +8 -2
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +11 -1
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +14 -0
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +56 -2
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts +2 -2
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +18 -11
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +7 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +6 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +15 -10
- package/dist/agent/run-store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +31 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +197 -1
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/index.js +76 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +30 -0
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/KeepTabOpenNotice.d.ts +9 -10
- package/dist/client/KeepTabOpenNotice.d.ts.map +1 -1
- package/dist/client/KeepTabOpenNotice.js +8 -4
- package/dist/client/KeepTabOpenNotice.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +14 -13
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +25 -17
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/history/VersionHistoryPanel.d.ts +1 -1
- package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -1
- package/dist/client/history/VersionHistoryPanel.js +12 -5
- package/dist/client/history/VersionHistoryPanel.js.map +1 -1
- package/dist/client/history/index.d.ts +1 -1
- package/dist/client/history/index.d.ts.map +1 -1
- package/dist/client/history/index.js +1 -1
- package/dist/client/history/index.js.map +1 -1
- package/dist/client/history/use-history.d.ts +12 -0
- package/dist/client/history/use-history.d.ts.map +1 -1
- package/dist/client/history/use-history.js +6 -0
- package/dist/client/history/use-history.js.map +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/review/index.d.ts +1 -1
- package/dist/client/review/index.d.ts.map +1 -1
- package/dist/client/review/index.js +1 -1
- package/dist/client/review/index.js.map +1 -1
- package/dist/client/review/use-review.d.ts +23 -0
- package/dist/client/review/use-review.d.ts.map +1 -1
- package/dist/client/review/use-review.js +8 -0
- package/dist/client/review/use-review.js.map +1 -1
- package/dist/client/use-run-stuck-detection.d.ts +1 -1
- package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
- package/dist/client/use-run-stuck-detection.js +6 -4
- package/dist/client/use-run-stuck-detection.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/history/actions/create-resource-version.js +7 -4
- package/dist/history/actions/create-resource-version.js.map +1 -1
- package/dist/history/index.d.ts +1 -1
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +1 -1
- package/dist/history/index.js.map +1 -1
- package/dist/history/registry.d.ts.map +1 -1
- package/dist/history/registry.js +14 -12
- package/dist/history/registry.js.map +1 -1
- package/dist/history/store.d.ts.map +1 -1
- package/dist/history/store.js +74 -49
- package/dist/history/store.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/review/actions/consume-review-feedback.d.ts +1 -0
- package/dist/review/actions/consume-review-feedback.js +8 -2
- package/dist/review/actions/consume-review-feedback.js.map +1 -1
- package/dist/review/actions/delete-review-comment.d.ts +2 -1
- package/dist/review/actions/delete-review-comment.js +11 -3
- package/dist/review/actions/delete-review-comment.js.map +1 -1
- package/dist/review/actions/resolve-review-thread.d.ts +2 -1
- package/dist/review/actions/resolve-review-thread.js +5 -2
- package/dist/review/actions/resolve-review-thread.js.map +1 -1
- package/dist/review/index.d.ts +1 -1
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +1 -1
- package/dist/review/index.js.map +1 -1
- package/dist/review/registry.d.ts.map +1 -1
- package/dist/review/registry.js +14 -12
- package/dist/review/registry.js.map +1 -1
- package/dist/review/store.d.ts +3 -3
- package/dist/review/store.d.ts.map +1 -1
- package/dist/review/store.js +7 -4
- package/dist/review/store.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +5 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/credential-provider.d.ts +2 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +13 -3
- package/dist/server/credential-provider.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
|
|
3
1
|
import { defineAction, embedApp } from "@agent-native/core";
|
|
4
2
|
import { writeAppState } from "@agent-native/core/application-state";
|
|
5
3
|
import { buildDeepLink } from "@agent-native/core/server";
|
|
6
|
-
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
7
4
|
import { eq } from "drizzle-orm";
|
|
8
5
|
import { z } from "zod";
|
|
9
6
|
|
|
@@ -89,28 +86,6 @@ function isLoopbackUrl(value: string): boolean {
|
|
|
89
86
|
);
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
/**
|
|
93
|
-
* Derive a stable per-user connection id for a devServerUrl + rootPath pair.
|
|
94
|
-
*
|
|
95
|
-
* The owner email is embedded in the hash so two users with an identical
|
|
96
|
-
* devServerUrl + rootPath (common with devcontainers/codespaces images) derive
|
|
97
|
-
* DIFFERENT ids and never collide on the shared primary key. Connections
|
|
98
|
-
* created before user scoping keep their old ids; those users simply
|
|
99
|
-
* reconnect fresh under the new per-user id.
|
|
100
|
-
*/
|
|
101
|
-
function stableConnectionId(
|
|
102
|
-
devServerUrl: string,
|
|
103
|
-
rootPath: string | undefined,
|
|
104
|
-
ownerEmail: string,
|
|
105
|
-
) {
|
|
106
|
-
const hash = crypto
|
|
107
|
-
.createHash("sha256")
|
|
108
|
-
.update(`${ownerEmail}\n${devServerUrl}\n${rootPath ?? ""}`)
|
|
109
|
-
.digest("base64url")
|
|
110
|
-
.slice(0, 16);
|
|
111
|
-
return `localhost_${hash}`;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
89
|
function designOverviewDeepLink(designId: string): string {
|
|
115
90
|
return buildDeepLink({
|
|
116
91
|
app: "design",
|
|
@@ -254,19 +229,11 @@ export default defineAction({
|
|
|
254
229
|
}) ?? [],
|
|
255
230
|
generatedAt: new Date().toISOString(),
|
|
256
231
|
};
|
|
257
|
-
let connectionId = args.connectionId;
|
|
258
|
-
if (!connectionId) {
|
|
259
|
-
const ownerEmail = getRequestUserEmail();
|
|
260
|
-
if (!ownerEmail) throw new Error("no authenticated user");
|
|
261
|
-
connectionId = stableConnectionId(
|
|
262
|
-
devServerUrl,
|
|
263
|
-
args.rootPath,
|
|
264
|
-
ownerEmail,
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
232
|
const connection = await connectLocalhostAction.run({
|
|
269
|
-
|
|
233
|
+
// Let connect-localhost be the single source of truth for stable
|
|
234
|
+
// per-user/per-org id derivation. Duplicating it here can create a second
|
|
235
|
+
// tokenless row after the CLI self-registers the bridge token.
|
|
236
|
+
id: args.connectionId,
|
|
270
237
|
name: args.name,
|
|
271
238
|
devServerUrl,
|
|
272
239
|
bridgeUrl: args.bridgeUrl,
|
|
@@ -351,6 +351,7 @@ interface DesignCanvasProps {
|
|
|
351
351
|
* For `"inline"` and `"localhost"` sources this prop is ignored.
|
|
352
352
|
*/
|
|
353
353
|
fusionUrl?: string;
|
|
354
|
+
bridgeToken?: string;
|
|
354
355
|
zoom: number;
|
|
355
356
|
onZoomChange?: (zoom: number) => void;
|
|
356
357
|
deviceFrame: DeviceFrameType;
|
|
@@ -371,6 +372,28 @@ interface DesignCanvasProps {
|
|
|
371
372
|
*/
|
|
372
373
|
runtimeReplacementContent?: string;
|
|
373
374
|
runtimeReplacementKey?: string;
|
|
375
|
+
styleRevertRequest?: {
|
|
376
|
+
requestId: number;
|
|
377
|
+
patches: Array<{
|
|
378
|
+
selector: string;
|
|
379
|
+
sourceId?: string | null;
|
|
380
|
+
styles: Record<string, string>;
|
|
381
|
+
}>;
|
|
382
|
+
} | null;
|
|
383
|
+
styleBaselineResetRequest?: number | null;
|
|
384
|
+
textRevertRequest?: {
|
|
385
|
+
requestId: number;
|
|
386
|
+
patches: Array<{
|
|
387
|
+
selector: string;
|
|
388
|
+
sourceId?: string | null;
|
|
389
|
+
value: string;
|
|
390
|
+
html?: string;
|
|
391
|
+
}>;
|
|
392
|
+
} | null;
|
|
393
|
+
structureAckRequest?: {
|
|
394
|
+
requestId: number;
|
|
395
|
+
acks: Array<{ requestId: string; applied: boolean }>;
|
|
396
|
+
} | null;
|
|
374
397
|
embeddedFrameBackground?: string;
|
|
375
398
|
transparentBackground?: boolean;
|
|
376
399
|
editorChromeScaleX?: number;
|
|
@@ -390,12 +413,17 @@ interface DesignCanvasProps {
|
|
|
390
413
|
selector: string,
|
|
391
414
|
styles: Record<string, string>,
|
|
392
415
|
info?: ElementInfo,
|
|
416
|
+
metadata?: { originalStyles?: Record<string, string> },
|
|
393
417
|
) => void;
|
|
394
418
|
onTextContentChange?: (
|
|
395
419
|
selector: string,
|
|
396
420
|
value: string,
|
|
397
421
|
info?: ElementInfo,
|
|
398
|
-
details?: {
|
|
422
|
+
details?: {
|
|
423
|
+
html?: string;
|
|
424
|
+
originalValue?: string;
|
|
425
|
+
originalHtml?: string;
|
|
426
|
+
},
|
|
399
427
|
) => void;
|
|
400
428
|
onTextEditingStateChange?: (state: {
|
|
401
429
|
active: boolean;
|
|
@@ -420,7 +448,7 @@ interface DesignCanvasProps {
|
|
|
420
448
|
sourceRect?: { x: number; y: number; width: number; height: number };
|
|
421
449
|
anchorRect?: { x: number; y: number; width: number; height: number };
|
|
422
450
|
},
|
|
423
|
-
) => boolean | void;
|
|
451
|
+
) => boolean | "pending" | void;
|
|
424
452
|
onVisualDuplicateChange?: (
|
|
425
453
|
selector: string,
|
|
426
454
|
cloneHtml: string,
|
|
@@ -837,6 +865,87 @@ function snapshotEndpointUrl(bridgeUrl: string, previewUrl: string): string {
|
|
|
837
865
|
return endpoint.toString();
|
|
838
866
|
}
|
|
839
867
|
|
|
868
|
+
export function liveEditEndpointUrl(
|
|
869
|
+
bridgeUrl: string,
|
|
870
|
+
previewUrl: string,
|
|
871
|
+
options?: { includeEditorBridge?: boolean },
|
|
872
|
+
): string {
|
|
873
|
+
const endpoint = new URL("/live-edit", bridgeUrl);
|
|
874
|
+
endpoint.searchParams.set("url", previewUrl);
|
|
875
|
+
if (options?.includeEditorBridge === false) {
|
|
876
|
+
endpoint.searchParams.set("bridge", "0");
|
|
877
|
+
}
|
|
878
|
+
return endpoint.toString();
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
function originFromUrl(value: string | undefined): string | null {
|
|
882
|
+
if (!value) return null;
|
|
883
|
+
try {
|
|
884
|
+
return new URL(value).origin;
|
|
885
|
+
} catch {
|
|
886
|
+
return null;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function buildEditorChromeBridgeScript(args: {
|
|
891
|
+
readOnly: boolean;
|
|
892
|
+
editMode: boolean;
|
|
893
|
+
editorChromeScaleX: number;
|
|
894
|
+
editorChromeScaleY: number;
|
|
895
|
+
screenId: string;
|
|
896
|
+
boardSurface: boolean;
|
|
897
|
+
}) {
|
|
898
|
+
return (
|
|
899
|
+
createEditorBridgeThemeScript(readEditorBridgeThemeVars()) +
|
|
900
|
+
EDITOR_CHROME_BRIDGE_SCRIPT.replace(
|
|
901
|
+
"__READ_ONLY__",
|
|
902
|
+
args.readOnly ? "true" : "false",
|
|
903
|
+
)
|
|
904
|
+
.replace("__TEXT_EDITING_ENABLED__", args.editMode ? "true" : "false")
|
|
905
|
+
.replace("__EDITOR_CHROME_SCALE_X__", String(args.editorChromeScaleX))
|
|
906
|
+
.replace("__EDITOR_CHROME_SCALE_Y__", String(args.editorChromeScaleY))
|
|
907
|
+
.replace("__DESIGN_CANVAS_SCREEN_ID__", JSON.stringify(args.screenId))
|
|
908
|
+
.replace(
|
|
909
|
+
"__DESIGN_CANVAS_BOARD_SURFACE__",
|
|
910
|
+
args.boardSurface ? "true" : "false",
|
|
911
|
+
)
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function contentHash(value: string): string {
|
|
916
|
+
let hash = 0;
|
|
917
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
918
|
+
hash = (hash * 31 + value.charCodeAt(index)) | 0;
|
|
919
|
+
}
|
|
920
|
+
return `${value.length}:${hash >>> 0}`;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
924
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function isFiniteRect(value: unknown): value is ElementInfo["boundingRect"] {
|
|
928
|
+
if (!isPlainRecord(value)) return false;
|
|
929
|
+
return (
|
|
930
|
+
Number.isFinite(value.x) &&
|
|
931
|
+
Number.isFinite(value.y) &&
|
|
932
|
+
Number.isFinite(value.width) &&
|
|
933
|
+
Number.isFinite(value.height)
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
export function isElementInfoPayload(value: unknown): value is ElementInfo {
|
|
938
|
+
if (!isPlainRecord(value)) return false;
|
|
939
|
+
return (
|
|
940
|
+
typeof value.tagName === "string" &&
|
|
941
|
+
Array.isArray(value.classes) &&
|
|
942
|
+
isPlainRecord(value.computedStyles) &&
|
|
943
|
+
isFiniteRect(value.boundingRect) &&
|
|
944
|
+
typeof value.isFlexChild === "boolean" &&
|
|
945
|
+
typeof value.isFlexContainer === "boolean"
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
|
|
840
949
|
/**
|
|
841
950
|
* Exponential backoff delay (ms) for the localhost bridge snapshot auto-retry
|
|
842
951
|
* loop, keyed by the zero-based retry attempt number (0 = first retry after
|
|
@@ -945,6 +1054,7 @@ export function DesignCanvas({
|
|
|
945
1054
|
externalSnapshotHtml,
|
|
946
1055
|
onExternalContentSnapshot,
|
|
947
1056
|
fusionUrl,
|
|
1057
|
+
bridgeToken,
|
|
948
1058
|
zoom,
|
|
949
1059
|
onZoomChange,
|
|
950
1060
|
deviceFrame,
|
|
@@ -952,6 +1062,10 @@ export function DesignCanvas({
|
|
|
952
1062
|
boardSurface = false,
|
|
953
1063
|
runtimeReplacementContent,
|
|
954
1064
|
runtimeReplacementKey,
|
|
1065
|
+
styleRevertRequest,
|
|
1066
|
+
styleBaselineResetRequest,
|
|
1067
|
+
textRevertRequest,
|
|
1068
|
+
structureAckRequest,
|
|
955
1069
|
embeddedFrameBackground,
|
|
956
1070
|
transparentBackground = false,
|
|
957
1071
|
editorChromeScaleX = 1,
|
|
@@ -1082,6 +1196,8 @@ export function DesignCanvas({
|
|
|
1082
1196
|
status: "loading" | "error";
|
|
1083
1197
|
message?: string;
|
|
1084
1198
|
} | null>(null);
|
|
1199
|
+
const [registeredLiveEditBridgeKey, setRegisteredLiveEditBridgeKey] =
|
|
1200
|
+
useState<string | null>(null);
|
|
1085
1201
|
const [externalSnapshotRetryNonce, setExternalSnapshotRetryNonce] =
|
|
1086
1202
|
useState(0);
|
|
1087
1203
|
// Exponential backoff for the auto-retry loop below: 1.5s → 3s → 6s →
|
|
@@ -1116,20 +1232,83 @@ export function DesignCanvas({
|
|
|
1116
1232
|
(fetchedExternalSnapshot?.url === rawExternalPreviewUrl
|
|
1117
1233
|
? fetchedExternalSnapshot.html
|
|
1118
1234
|
: undefined);
|
|
1119
|
-
const
|
|
1235
|
+
const editorChromeBridgeForCurrentState = useMemo(
|
|
1236
|
+
() =>
|
|
1237
|
+
buildEditorChromeBridgeScript({
|
|
1238
|
+
readOnly,
|
|
1239
|
+
editMode,
|
|
1240
|
+
editorChromeScaleX: effectiveEditorChromeScaleX,
|
|
1241
|
+
editorChromeScaleY: effectiveEditorChromeScaleY,
|
|
1242
|
+
screenId: screenId ?? contentKey ?? "",
|
|
1243
|
+
boardSurface,
|
|
1244
|
+
}),
|
|
1245
|
+
[
|
|
1246
|
+
boardSurface,
|
|
1247
|
+
contentKey,
|
|
1248
|
+
editMode,
|
|
1249
|
+
effectiveEditorChromeScaleX,
|
|
1250
|
+
effectiveEditorChromeScaleY,
|
|
1251
|
+
readOnly,
|
|
1252
|
+
screenId,
|
|
1253
|
+
],
|
|
1254
|
+
);
|
|
1255
|
+
const embeddedWheelBridgeForCurrentState = useMemo(
|
|
1256
|
+
() =>
|
|
1257
|
+
EMBEDDED_WHEEL_BRIDGE_SCRIPT.replace(
|
|
1258
|
+
"__EMBEDDED_WHEEL_FORWARDING_ENABLED__",
|
|
1259
|
+
isEmbeddedFrame ? "true" : "false",
|
|
1260
|
+
),
|
|
1261
|
+
[isEmbeddedFrame],
|
|
1262
|
+
);
|
|
1263
|
+
const liveEditBridgeScript = useMemo(
|
|
1264
|
+
() =>
|
|
1265
|
+
MOTION_PREVIEW_BRIDGE_SCRIPT +
|
|
1266
|
+
SHADER_FILL_PREVIEW_BRIDGE_SCRIPT +
|
|
1267
|
+
TWEAK_BRIDGE_SCRIPT +
|
|
1268
|
+
ZOOM_BRIDGE_SCRIPT +
|
|
1269
|
+
LIGHTWEIGHT_HIT_TEST_BRIDGE_SCRIPT +
|
|
1270
|
+
embeddedWheelBridgeForCurrentState +
|
|
1271
|
+
editorChromeBridgeForCurrentState,
|
|
1272
|
+
[editorChromeBridgeForCurrentState, embeddedWheelBridgeForCurrentState],
|
|
1273
|
+
);
|
|
1274
|
+
const liveEditBridgeKey = useMemo(
|
|
1275
|
+
() => contentHash(liveEditBridgeScript),
|
|
1276
|
+
[liveEditBridgeScript],
|
|
1277
|
+
);
|
|
1278
|
+
const hasLiveEditExternalFrame =
|
|
1279
|
+
sourceType === "localhost" && Boolean(bridgeUrl && rawExternalPreviewUrl);
|
|
1280
|
+
const usesLiveEditEditorBridge =
|
|
1120
1281
|
sourceType === "localhost" &&
|
|
1121
1282
|
Boolean(bridgeUrl && rawExternalPreviewUrl) &&
|
|
1122
1283
|
!interactMode &&
|
|
1123
1284
|
!readOnly;
|
|
1285
|
+
const liveEditBridgeRegistered =
|
|
1286
|
+
usesLiveEditEditorBridge &&
|
|
1287
|
+
registeredLiveEditBridgeKey === liveEditBridgeKey;
|
|
1288
|
+
const requiresEditableExternalSnapshot = false;
|
|
1289
|
+
const liveEditExternalPreviewUrl =
|
|
1290
|
+
hasLiveEditExternalFrame &&
|
|
1291
|
+
bridgeUrl &&
|
|
1292
|
+
rawExternalPreviewUrl &&
|
|
1293
|
+
interactMode
|
|
1294
|
+
? liveEditEndpointUrl(bridgeUrl, rawExternalPreviewUrl, {
|
|
1295
|
+
includeEditorBridge: false,
|
|
1296
|
+
})
|
|
1297
|
+
: liveEditBridgeRegistered && bridgeUrl && rawExternalPreviewUrl
|
|
1298
|
+
? liveEditEndpointUrl(bridgeUrl, rawExternalPreviewUrl)
|
|
1299
|
+
: null;
|
|
1124
1300
|
const iframeRenderContent =
|
|
1125
1301
|
activeExternalSnapshotHtml ??
|
|
1126
1302
|
(requiresEditableExternalSnapshot ? "" : renderedContent);
|
|
1127
1303
|
const externalPreviewUrl =
|
|
1128
|
-
|
|
1304
|
+
liveEditExternalPreviewUrl ??
|
|
1305
|
+
(activeExternalSnapshotHtml || requiresEditableExternalSnapshot
|
|
1129
1306
|
? null
|
|
1130
|
-
: rawExternalPreviewUrl;
|
|
1307
|
+
: rawExternalPreviewUrl);
|
|
1131
1308
|
const waitingForEditableExternalSnapshot =
|
|
1132
1309
|
requiresEditableExternalSnapshot && !activeExternalSnapshotHtml;
|
|
1310
|
+
const waitingForLiveEditBridge =
|
|
1311
|
+
usesLiveEditEditorBridge && !liveEditBridgeRegistered;
|
|
1133
1312
|
zoomRef.current = zoom;
|
|
1134
1313
|
runtimeReplacementContentRef.current = runtimeReplacementContent;
|
|
1135
1314
|
runtimeReplacementKeyRef.current = runtimeReplacementKey;
|
|
@@ -1138,9 +1317,61 @@ export function DesignCanvas({
|
|
|
1138
1317
|
onExternalContentSnapshotRef.current = onExternalContentSnapshot;
|
|
1139
1318
|
}, [onExternalContentSnapshot]);
|
|
1140
1319
|
|
|
1320
|
+
useEffect(() => {
|
|
1321
|
+
if (!usesLiveEditEditorBridge || !bridgeUrl) {
|
|
1322
|
+
setRegisteredLiveEditBridgeKey(null);
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
let cancelled = false;
|
|
1326
|
+
setRegisteredLiveEditBridgeKey((current) =>
|
|
1327
|
+
current === liveEditBridgeKey ? current : null,
|
|
1328
|
+
);
|
|
1329
|
+
void (async () => {
|
|
1330
|
+
try {
|
|
1331
|
+
const endpoint = new URL("/live-edit-bridge", bridgeUrl).toString();
|
|
1332
|
+
const response = await fetch(endpoint, {
|
|
1333
|
+
method: "POST",
|
|
1334
|
+
headers: {
|
|
1335
|
+
"content-type": "application/json",
|
|
1336
|
+
...(bridgeToken ? { "x-bridge-token": bridgeToken } : {}),
|
|
1337
|
+
},
|
|
1338
|
+
body: JSON.stringify({ script: liveEditBridgeScript }),
|
|
1339
|
+
});
|
|
1340
|
+
if (!response.ok) {
|
|
1341
|
+
throw new Error(`Bridge registration failed (${response.status})`);
|
|
1342
|
+
}
|
|
1343
|
+
if (!cancelled) {
|
|
1344
|
+
setRegisteredLiveEditBridgeKey(liveEditBridgeKey);
|
|
1345
|
+
}
|
|
1346
|
+
} catch (error) {
|
|
1347
|
+
if (!cancelled) {
|
|
1348
|
+
console.warn(
|
|
1349
|
+
"[DesignCanvas] live edit bridge registration failed",
|
|
1350
|
+
error,
|
|
1351
|
+
);
|
|
1352
|
+
setRegisteredLiveEditBridgeKey(null);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
})();
|
|
1356
|
+
return () => {
|
|
1357
|
+
cancelled = true;
|
|
1358
|
+
};
|
|
1359
|
+
}, [
|
|
1360
|
+
bridgeUrl,
|
|
1361
|
+
bridgeToken,
|
|
1362
|
+
liveEditBridgeKey,
|
|
1363
|
+
liveEditBridgeScript,
|
|
1364
|
+
usesLiveEditEditorBridge,
|
|
1365
|
+
]);
|
|
1366
|
+
|
|
1141
1367
|
useEffect(() => {
|
|
1142
1368
|
const previewUrl = rawExternalPreviewUrl;
|
|
1143
|
-
if (
|
|
1369
|
+
if (
|
|
1370
|
+
!requiresEditableExternalSnapshot ||
|
|
1371
|
+
sourceType !== "localhost" ||
|
|
1372
|
+
!bridgeUrl ||
|
|
1373
|
+
!previewUrl
|
|
1374
|
+
) {
|
|
1144
1375
|
snapshotRetryAttemptRef.current = 0;
|
|
1145
1376
|
setFetchedExternalSnapshot((current) =>
|
|
1146
1377
|
current?.url === previewUrl ? current : null,
|
|
@@ -1468,6 +1699,12 @@ export function DesignCanvas({
|
|
|
1468
1699
|
origin: e.origin,
|
|
1469
1700
|
iframeWindow,
|
|
1470
1701
|
parentOrigin: window.location.origin,
|
|
1702
|
+
allowedOrigins:
|
|
1703
|
+
sourceType === "localhost" && bridgeUrl
|
|
1704
|
+
? [originFromUrl(bridgeUrl)].filter(
|
|
1705
|
+
(origin): origin is string => Boolean(origin),
|
|
1706
|
+
)
|
|
1707
|
+
: [],
|
|
1471
1708
|
});
|
|
1472
1709
|
if (!trusted) {
|
|
1473
1710
|
return;
|
|
@@ -1509,8 +1746,19 @@ export function DesignCanvas({
|
|
|
1509
1746
|
e.data.styles && typeof e.data.styles === "object"
|
|
1510
1747
|
? (e.data.styles as Record<string, string>)
|
|
1511
1748
|
: {};
|
|
1749
|
+
const originalStyles =
|
|
1750
|
+
e.data.originalStyles && typeof e.data.originalStyles === "object"
|
|
1751
|
+
? (e.data.originalStyles as Record<string, string>)
|
|
1752
|
+
: undefined;
|
|
1512
1753
|
if (selector && Object.keys(styles).length > 0) {
|
|
1513
|
-
onVisualStyleChange?.(
|
|
1754
|
+
onVisualStyleChange?.(
|
|
1755
|
+
selector,
|
|
1756
|
+
styles,
|
|
1757
|
+
isElementInfoPayload(e.data.payload) ? e.data.payload : undefined,
|
|
1758
|
+
{
|
|
1759
|
+
originalStyles,
|
|
1760
|
+
},
|
|
1761
|
+
);
|
|
1514
1762
|
}
|
|
1515
1763
|
return;
|
|
1516
1764
|
}
|
|
@@ -1528,8 +1776,20 @@ export function DesignCanvas({
|
|
|
1528
1776
|
const value = String(e.data.value ?? "");
|
|
1529
1777
|
const html =
|
|
1530
1778
|
typeof e.data.html === "string" ? String(e.data.html) : undefined;
|
|
1779
|
+
const originalValue =
|
|
1780
|
+
typeof e.data.originalValue === "string"
|
|
1781
|
+
? String(e.data.originalValue)
|
|
1782
|
+
: undefined;
|
|
1783
|
+
const originalHtml =
|
|
1784
|
+
typeof e.data.originalHtml === "string"
|
|
1785
|
+
? String(e.data.originalHtml)
|
|
1786
|
+
: undefined;
|
|
1531
1787
|
if (selector) {
|
|
1532
|
-
onTextContentChange?.(selector, value, e.data.payload, {
|
|
1788
|
+
onTextContentChange?.(selector, value, e.data.payload, {
|
|
1789
|
+
html,
|
|
1790
|
+
originalValue,
|
|
1791
|
+
originalHtml,
|
|
1792
|
+
});
|
|
1533
1793
|
}
|
|
1534
1794
|
return;
|
|
1535
1795
|
}
|
|
@@ -1599,7 +1859,7 @@ export function DesignCanvas({
|
|
|
1599
1859
|
anchorRect,
|
|
1600
1860
|
},
|
|
1601
1861
|
);
|
|
1602
|
-
if (requestId) {
|
|
1862
|
+
if (requestId && applied !== "pending") {
|
|
1603
1863
|
iframeRef.current?.contentWindow?.postMessage(
|
|
1604
1864
|
{
|
|
1605
1865
|
type: "visual-structure-ack",
|
|
@@ -1828,6 +2088,7 @@ export function DesignCanvas({
|
|
|
1828
2088
|
onComponentSourceJump,
|
|
1829
2089
|
isEmbeddedFrame,
|
|
1830
2090
|
sourceType,
|
|
2091
|
+
bridgeUrl,
|
|
1831
2092
|
fusionUrl,
|
|
1832
2093
|
flushPendingOneShotMessages,
|
|
1833
2094
|
]);
|
|
@@ -2159,6 +2420,89 @@ export function DesignCanvas({
|
|
|
2159
2420
|
[postOneShotBridgeMessage],
|
|
2160
2421
|
);
|
|
2161
2422
|
|
|
2423
|
+
const lastStyleRevertRequestIdRef = useRef<number | null>(null);
|
|
2424
|
+
useEffect(() => {
|
|
2425
|
+
if (!styleRevertRequest) return;
|
|
2426
|
+
if (lastStyleRevertRequestIdRef.current === styleRevertRequest.requestId) {
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
lastStyleRevertRequestIdRef.current = styleRevertRequest.requestId;
|
|
2430
|
+
for (const patch of styleRevertRequest.patches) {
|
|
2431
|
+
const selectorCandidates = [
|
|
2432
|
+
patch.selector,
|
|
2433
|
+
patch.sourceId
|
|
2434
|
+
? `[data-agent-native-node-id="${String(patch.sourceId).replace(/"/g, '\\"')}"]`
|
|
2435
|
+
: "",
|
|
2436
|
+
].filter(Boolean);
|
|
2437
|
+
for (const [property, value] of Object.entries(patch.styles)) {
|
|
2438
|
+
postOneShotBridgeMessage({
|
|
2439
|
+
type: "style-change",
|
|
2440
|
+
selector: patch.selector,
|
|
2441
|
+
property,
|
|
2442
|
+
value,
|
|
2443
|
+
selectorCandidates,
|
|
2444
|
+
nodeId: patch.sourceId ?? "",
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}, [postOneShotBridgeMessage, styleRevertRequest]);
|
|
2449
|
+
|
|
2450
|
+
const lastStyleBaselineResetRequestRef = useRef<number | null>(null);
|
|
2451
|
+
useEffect(() => {
|
|
2452
|
+
if (styleBaselineResetRequest == null) return;
|
|
2453
|
+
if (
|
|
2454
|
+
lastStyleBaselineResetRequestRef.current === styleBaselineResetRequest
|
|
2455
|
+
) {
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
lastStyleBaselineResetRequestRef.current = styleBaselineResetRequest;
|
|
2459
|
+
postOneShotBridgeMessage({
|
|
2460
|
+
type: "agent-native:reset-live-visual-edit-baselines",
|
|
2461
|
+
});
|
|
2462
|
+
}, [postOneShotBridgeMessage, styleBaselineResetRequest]);
|
|
2463
|
+
|
|
2464
|
+
const lastTextRevertRequestIdRef = useRef<number | null>(null);
|
|
2465
|
+
useEffect(() => {
|
|
2466
|
+
if (!textRevertRequest) return;
|
|
2467
|
+
if (lastTextRevertRequestIdRef.current === textRevertRequest.requestId) {
|
|
2468
|
+
return;
|
|
2469
|
+
}
|
|
2470
|
+
lastTextRevertRequestIdRef.current = textRevertRequest.requestId;
|
|
2471
|
+
for (const patch of textRevertRequest.patches) {
|
|
2472
|
+
const selectorCandidates = [
|
|
2473
|
+
patch.selector,
|
|
2474
|
+
patch.sourceId
|
|
2475
|
+
? `[data-agent-native-node-id="${String(patch.sourceId).replace(/"/g, '\\"')}"]`
|
|
2476
|
+
: "",
|
|
2477
|
+
].filter(Boolean);
|
|
2478
|
+
postOneShotBridgeMessage({
|
|
2479
|
+
type: "set-text-content",
|
|
2480
|
+
selector: patch.selector,
|
|
2481
|
+
selectorCandidates,
|
|
2482
|
+
value: patch.value,
|
|
2483
|
+
html: patch.html,
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
}, [postOneShotBridgeMessage, textRevertRequest]);
|
|
2487
|
+
|
|
2488
|
+
const lastStructureAckRequestIdRef = useRef<number | null>(null);
|
|
2489
|
+
useEffect(() => {
|
|
2490
|
+
if (!structureAckRequest) return;
|
|
2491
|
+
if (
|
|
2492
|
+
lastStructureAckRequestIdRef.current === structureAckRequest.requestId
|
|
2493
|
+
) {
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
lastStructureAckRequestIdRef.current = structureAckRequest.requestId;
|
|
2497
|
+
for (const ack of structureAckRequest.acks) {
|
|
2498
|
+
postOneShotBridgeMessage({
|
|
2499
|
+
type: "visual-structure-ack",
|
|
2500
|
+
requestId: ack.requestId,
|
|
2501
|
+
applied: ack.applied,
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
}, [postOneShotBridgeMessage, structureAckRequest]);
|
|
2505
|
+
|
|
2162
2506
|
/**
|
|
2163
2507
|
* Send a motion-preview scrub tick to the iframe. `t` is the normalised
|
|
2164
2508
|
* playhead position in [0, 1]. Tracks must have been loaded first via the
|
|
@@ -2675,9 +3019,15 @@ export function DesignCanvas({
|
|
|
2675
3019
|
</div>
|
|
2676
3020
|
</div>
|
|
2677
3021
|
) : null}
|
|
2678
|
-
{waitingForEditableExternalSnapshot ? (
|
|
2679
|
-
<div className="pointer-events-
|
|
2680
|
-
{
|
|
3022
|
+
{waitingForEditableExternalSnapshot || waitingForLiveEditBridge ? (
|
|
3023
|
+
<div className="pointer-events-auto absolute inset-0 z-10 flex items-center justify-center bg-background/85 px-4 text-center text-sm text-muted-foreground">
|
|
3024
|
+
{waitingForLiveEditBridge ? (
|
|
3025
|
+
<div className="max-w-[28rem] rounded-md border bg-card px-4 py-3 shadow-sm">
|
|
3026
|
+
{
|
|
3027
|
+
"Preparing live editor..." /* i18n-ignore transient localhost live-edit bridge loading state */
|
|
3028
|
+
}
|
|
3029
|
+
</div>
|
|
3030
|
+
) : externalSnapshotState?.status === "error" ? (
|
|
2681
3031
|
// A real offline dev server previously looked identical to the
|
|
2682
3032
|
// ordinary "still loading" state and retried forever on a fixed
|
|
2683
3033
|
// 1.5s timer — see getSnapshotRetryDelayMs for the backoff that
|