@agent-native/core 0.90.3 → 0.90.4

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.
Files changed (191) hide show
  1. package/bin/agent-native.js +25 -3
  2. package/corpus/README.md +1 -1
  3. package/corpus/core/CHANGELOG.md +9 -0
  4. package/corpus/core/bin/agent-native.js +25 -3
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/agent/engine/index.ts +1 -0
  7. package/corpus/core/src/agent/engine/registry.ts +63 -2
  8. package/corpus/core/src/cli/create.ts +35 -0
  9. package/corpus/core/src/cli/design-connect.ts +248 -0
  10. package/corpus/core/src/cli/index.ts +84 -7
  11. package/corpus/core/src/cli/workspacify.ts +34 -0
  12. package/corpus/core/src/client/history/VersionHistoryPanel.tsx +77 -47
  13. package/corpus/core/src/client/history/index.ts +3 -0
  14. package/corpus/core/src/client/history/use-history.ts +28 -0
  15. package/corpus/core/src/client/index.ts +8 -0
  16. package/corpus/core/src/client/review/index.ts +5 -0
  17. package/corpus/core/src/client/review/use-review.ts +40 -2
  18. package/corpus/core/src/history/actions/create-resource-version.ts +11 -8
  19. package/corpus/core/src/history/index.ts +0 -1
  20. package/corpus/core/src/history/registry.ts +24 -12
  21. package/corpus/core/src/history/store.ts +84 -53
  22. package/corpus/core/src/review/actions/consume-review-feedback.ts +15 -5
  23. package/corpus/core/src/review/actions/delete-review-comment.ts +15 -3
  24. package/corpus/core/src/review/actions/resolve-review-thread.ts +12 -5
  25. package/corpus/core/src/review/index.ts +0 -5
  26. package/corpus/core/src/review/registry.ts +24 -12
  27. package/corpus/core/src/review/store.ts +10 -7
  28. package/corpus/core/src/server/agent-chat-plugin.ts +6 -0
  29. package/corpus/core/src/server/credential-provider.ts +13 -3
  30. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +11 -11
  31. package/corpus/templates/assets/actions/generate-image.ts +64 -28
  32. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +1 -1
  33. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +91 -74
  34. package/corpus/templates/assets/server/lib/generation.ts +29 -4
  35. package/corpus/templates/assets/server/lib/image-processing.ts +131 -0
  36. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +24 -2
  37. package/corpus/templates/calendar/AGENTS.md +3 -0
  38. package/corpus/templates/calendar/actions/create-event.ts +8 -34
  39. package/corpus/templates/calendar/actions/event-action-helpers.ts +52 -0
  40. package/corpus/templates/calendar/actions/manage-event-draft.ts +7 -34
  41. package/corpus/templates/calendar/actions/update-event.ts +10 -23
  42. package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +47 -0
  43. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +23 -0
  44. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +59 -2
  45. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +40 -1
  46. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +34 -1
  47. package/corpus/templates/calendar/app/hooks/use-events.ts +6 -0
  48. package/corpus/templates/calendar/app/i18n/zh-TW.ts +4 -0
  49. package/corpus/templates/calendar/app/i18n-data.ts +40 -0
  50. package/corpus/templates/calendar/changelog/2026-07-07-you-can-mark-event-guests-as-optional-while-inviting-them-or.md +6 -0
  51. package/corpus/templates/calendar/server/handlers/events.ts +1 -0
  52. package/corpus/templates/calendar/server/lib/google-calendar.ts +4 -0
  53. package/corpus/templates/calendar/server/plugins/agent-chat.ts +1 -1
  54. package/corpus/templates/calendar/shared/api.ts +2 -0
  55. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +29 -3
  56. package/corpus/templates/clips/actions/generate-workflow.ts +19 -8
  57. package/corpus/templates/clips/actions/get-clips-ai-prefs.ts +36 -0
  58. package/corpus/templates/clips/actions/lib/clips-ai-prefs.ts +28 -0
  59. package/corpus/templates/clips/actions/regenerate-chapters.ts +21 -7
  60. package/corpus/templates/clips/actions/regenerate-summary.ts +26 -8
  61. package/corpus/templates/clips/actions/regenerate-title.ts +44 -7
  62. package/corpus/templates/clips/actions/update-clips-ai-prefs.ts +50 -0
  63. package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +7 -2
  64. package/corpus/templates/clips/app/hooks/use-auto-title.ts +25 -10
  65. package/corpus/templates/clips/app/i18n/ar-SA.ts +28 -6
  66. package/corpus/templates/clips/app/i18n/de-DE.ts +28 -6
  67. package/corpus/templates/clips/app/i18n/en-US.ts +29 -6
  68. package/corpus/templates/clips/app/i18n/es-ES.ts +29 -6
  69. package/corpus/templates/clips/app/i18n/fr-FR.ts +30 -6
  70. package/corpus/templates/clips/app/i18n/hi-IN.ts +26 -6
  71. package/corpus/templates/clips/app/i18n/ja-JP.ts +27 -6
  72. package/corpus/templates/clips/app/i18n/ko-KR.ts +28 -6
  73. package/corpus/templates/clips/app/i18n/pt-BR.ts +28 -6
  74. package/corpus/templates/clips/app/i18n/zh-CN.ts +26 -6
  75. package/corpus/templates/clips/app/i18n/zh-TW.ts +25 -6
  76. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +73 -0
  77. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -0
  78. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -2
  79. package/corpus/templates/clips/changelog/2026-07-07-ai-tools-can-include-the-full-video-not-just-the-transcript-.md +6 -0
  80. package/corpus/templates/clips/changelog/2026-07-07-clips-desktop-now-surfaces-meetings-and-dictate-directly-in-.md +6 -0
  81. package/corpus/templates/clips/changelog/2026-07-07-meeting-notes-now-explain-exactly-how-to-start-granola-style.md +6 -0
  82. package/corpus/templates/clips/changelog/2026-07-07-meeting-start-alerts-now-include-separate-start-notes-and-jo.md +6 -0
  83. package/corpus/templates/clips/changelog/2026-07-07-recordings-start-more-predictably-and-newly-saved-clips-refr.md +6 -0
  84. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  85. package/corpus/templates/clips/desktop/src/app.tsx +435 -105
  86. package/corpus/templates/clips/desktop/src/lib/recorder.ts +27 -21
  87. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -22
  88. package/corpus/templates/clips/desktop/src/styles.css +167 -2
  89. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  90. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -7
  91. package/corpus/templates/clips/desktop/src-tauri/src/tray_meetings.rs +5 -4
  92. package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.put.ts +16 -8
  93. package/corpus/templates/clips/shared/clips-ai-prefs.ts +81 -0
  94. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +16 -1
  95. package/corpus/templates/design/actions/add-localhost-screens.ts +1 -0
  96. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +345 -11
  97. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +89 -14
  98. package/corpus/templates/design/app/components/design/bridge-security.ts +7 -1
  99. package/corpus/templates/design/app/components/design/multi-screen/types.ts +1 -1
  100. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
  101. package/corpus/templates/design/app/i18n-data.ts +41 -0
  102. package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-uses-live-app-iframes-for-click-to-edit-pr.md +6 -0
  103. package/dist/agent/engine/index.d.ts +1 -1
  104. package/dist/agent/engine/index.d.ts.map +1 -1
  105. package/dist/agent/engine/index.js +1 -1
  106. package/dist/agent/engine/index.js.map +1 -1
  107. package/dist/agent/engine/registry.d.ts +14 -0
  108. package/dist/agent/engine/registry.d.ts.map +1 -1
  109. package/dist/agent/engine/registry.js +56 -2
  110. package/dist/agent/engine/registry.js.map +1 -1
  111. package/dist/cli/create.d.ts.map +1 -1
  112. package/dist/cli/create.js +31 -0
  113. package/dist/cli/create.js.map +1 -1
  114. package/dist/cli/design-connect.d.ts.map +1 -1
  115. package/dist/cli/design-connect.js +186 -0
  116. package/dist/cli/design-connect.js.map +1 -1
  117. package/dist/cli/index.js +76 -9
  118. package/dist/cli/index.js.map +1 -1
  119. package/dist/cli/workspacify.d.ts.map +1 -1
  120. package/dist/cli/workspacify.js +30 -0
  121. package/dist/cli/workspacify.js.map +1 -1
  122. package/dist/client/history/VersionHistoryPanel.d.ts +1 -1
  123. package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -1
  124. package/dist/client/history/VersionHistoryPanel.js +12 -5
  125. package/dist/client/history/VersionHistoryPanel.js.map +1 -1
  126. package/dist/client/history/index.d.ts +1 -1
  127. package/dist/client/history/index.d.ts.map +1 -1
  128. package/dist/client/history/index.js +1 -1
  129. package/dist/client/history/index.js.map +1 -1
  130. package/dist/client/history/use-history.d.ts +12 -0
  131. package/dist/client/history/use-history.d.ts.map +1 -1
  132. package/dist/client/history/use-history.js +6 -0
  133. package/dist/client/history/use-history.js.map +1 -1
  134. package/dist/client/index.d.ts +2 -2
  135. package/dist/client/index.d.ts.map +1 -1
  136. package/dist/client/index.js +2 -2
  137. package/dist/client/index.js.map +1 -1
  138. package/dist/client/review/index.d.ts +1 -1
  139. package/dist/client/review/index.d.ts.map +1 -1
  140. package/dist/client/review/index.js +1 -1
  141. package/dist/client/review/index.js.map +1 -1
  142. package/dist/client/review/use-review.d.ts +23 -0
  143. package/dist/client/review/use-review.d.ts.map +1 -1
  144. package/dist/client/review/use-review.js +8 -0
  145. package/dist/client/review/use-review.js.map +1 -1
  146. package/dist/collab/awareness.d.ts +2 -2
  147. package/dist/collab/awareness.d.ts.map +1 -1
  148. package/dist/collab/routes.d.ts +1 -1
  149. package/dist/history/actions/create-resource-version.js +7 -4
  150. package/dist/history/actions/create-resource-version.js.map +1 -1
  151. package/dist/history/index.d.ts +1 -1
  152. package/dist/history/index.d.ts.map +1 -1
  153. package/dist/history/index.js +1 -1
  154. package/dist/history/index.js.map +1 -1
  155. package/dist/history/registry.d.ts.map +1 -1
  156. package/dist/history/registry.js +14 -12
  157. package/dist/history/registry.js.map +1 -1
  158. package/dist/history/store.d.ts.map +1 -1
  159. package/dist/history/store.js +74 -49
  160. package/dist/history/store.js.map +1 -1
  161. package/dist/observability/routes.d.ts +3 -3
  162. package/dist/progress/routes.d.ts +1 -1
  163. package/dist/review/actions/consume-review-feedback.d.ts +1 -0
  164. package/dist/review/actions/consume-review-feedback.js +8 -2
  165. package/dist/review/actions/consume-review-feedback.js.map +1 -1
  166. package/dist/review/actions/delete-review-comment.d.ts +2 -1
  167. package/dist/review/actions/delete-review-comment.js +11 -3
  168. package/dist/review/actions/delete-review-comment.js.map +1 -1
  169. package/dist/review/actions/resolve-review-thread.d.ts +2 -1
  170. package/dist/review/actions/resolve-review-thread.js +5 -2
  171. package/dist/review/actions/resolve-review-thread.js.map +1 -1
  172. package/dist/review/index.d.ts +1 -1
  173. package/dist/review/index.d.ts.map +1 -1
  174. package/dist/review/index.js +1 -1
  175. package/dist/review/index.js.map +1 -1
  176. package/dist/review/registry.d.ts.map +1 -1
  177. package/dist/review/registry.js +14 -12
  178. package/dist/review/registry.js.map +1 -1
  179. package/dist/review/store.d.ts +3 -3
  180. package/dist/review/store.d.ts.map +1 -1
  181. package/dist/review/store.js +7 -4
  182. package/dist/review/store.js.map +1 -1
  183. package/dist/secrets/routes.d.ts +9 -9
  184. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  185. package/dist/server/agent-chat-plugin.js +5 -0
  186. package/dist/server/agent-chat-plugin.js.map +1 -1
  187. package/dist/server/credential-provider.d.ts +2 -0
  188. package/dist/server/credential-provider.d.ts.map +1 -1
  189. package/dist/server/credential-provider.js +13 -3
  190. package/dist/server/credential-provider.js.map +1 -1
  191. package/package.json +1 -1
@@ -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?: { html?: string },
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,80 @@ function snapshotEndpointUrl(bridgeUrl: string, previewUrl: string): string {
837
865
  return endpoint.toString();
838
866
  }
839
867
 
868
+ function liveEditEndpointUrl(bridgeUrl: string, previewUrl: string): string {
869
+ const endpoint = new URL("/live-edit", bridgeUrl);
870
+ endpoint.searchParams.set("url", previewUrl);
871
+ return endpoint.toString();
872
+ }
873
+
874
+ function originFromUrl(value: string | undefined): string | null {
875
+ if (!value) return null;
876
+ try {
877
+ return new URL(value).origin;
878
+ } catch {
879
+ return null;
880
+ }
881
+ }
882
+
883
+ function buildEditorChromeBridgeScript(args: {
884
+ readOnly: boolean;
885
+ editMode: boolean;
886
+ editorChromeScaleX: number;
887
+ editorChromeScaleY: number;
888
+ screenId: string;
889
+ boardSurface: boolean;
890
+ }) {
891
+ return (
892
+ createEditorBridgeThemeScript(readEditorBridgeThemeVars()) +
893
+ EDITOR_CHROME_BRIDGE_SCRIPT.replace(
894
+ "__READ_ONLY__",
895
+ args.readOnly ? "true" : "false",
896
+ )
897
+ .replace("__TEXT_EDITING_ENABLED__", args.editMode ? "true" : "false")
898
+ .replace("__EDITOR_CHROME_SCALE_X__", String(args.editorChromeScaleX))
899
+ .replace("__EDITOR_CHROME_SCALE_Y__", String(args.editorChromeScaleY))
900
+ .replace("__DESIGN_CANVAS_SCREEN_ID__", JSON.stringify(args.screenId))
901
+ .replace(
902
+ "__DESIGN_CANVAS_BOARD_SURFACE__",
903
+ args.boardSurface ? "true" : "false",
904
+ )
905
+ );
906
+ }
907
+
908
+ function contentHash(value: string): string {
909
+ let hash = 0;
910
+ for (let index = 0; index < value.length; index += 1) {
911
+ hash = (hash * 31 + value.charCodeAt(index)) | 0;
912
+ }
913
+ return `${value.length}:${hash >>> 0}`;
914
+ }
915
+
916
+ function isPlainRecord(value: unknown): value is Record<string, unknown> {
917
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
918
+ }
919
+
920
+ function isFiniteRect(value: unknown): value is ElementInfo["boundingRect"] {
921
+ if (!isPlainRecord(value)) return false;
922
+ return (
923
+ Number.isFinite(value.x) &&
924
+ Number.isFinite(value.y) &&
925
+ Number.isFinite(value.width) &&
926
+ Number.isFinite(value.height)
927
+ );
928
+ }
929
+
930
+ export function isElementInfoPayload(value: unknown): value is ElementInfo {
931
+ if (!isPlainRecord(value)) return false;
932
+ return (
933
+ typeof value.tagName === "string" &&
934
+ Array.isArray(value.classes) &&
935
+ isPlainRecord(value.computedStyles) &&
936
+ isFiniteRect(value.boundingRect) &&
937
+ typeof value.isFlexChild === "boolean" &&
938
+ typeof value.isFlexContainer === "boolean"
939
+ );
940
+ }
941
+
840
942
  /**
841
943
  * Exponential backoff delay (ms) for the localhost bridge snapshot auto-retry
842
944
  * loop, keyed by the zero-based retry attempt number (0 = first retry after
@@ -945,6 +1047,7 @@ export function DesignCanvas({
945
1047
  externalSnapshotHtml,
946
1048
  onExternalContentSnapshot,
947
1049
  fusionUrl,
1050
+ bridgeToken,
948
1051
  zoom,
949
1052
  onZoomChange,
950
1053
  deviceFrame,
@@ -952,6 +1055,10 @@ export function DesignCanvas({
952
1055
  boardSurface = false,
953
1056
  runtimeReplacementContent,
954
1057
  runtimeReplacementKey,
1058
+ styleRevertRequest,
1059
+ styleBaselineResetRequest,
1060
+ textRevertRequest,
1061
+ structureAckRequest,
955
1062
  embeddedFrameBackground,
956
1063
  transparentBackground = false,
957
1064
  editorChromeScaleX = 1,
@@ -1082,6 +1189,8 @@ export function DesignCanvas({
1082
1189
  status: "loading" | "error";
1083
1190
  message?: string;
1084
1191
  } | null>(null);
1192
+ const [registeredLiveEditBridgeKey, setRegisteredLiveEditBridgeKey] =
1193
+ useState<string | null>(null);
1085
1194
  const [externalSnapshotRetryNonce, setExternalSnapshotRetryNonce] =
1086
1195
  useState(0);
1087
1196
  // Exponential backoff for the auto-retry loop below: 1.5s → 3s → 6s →
@@ -1116,20 +1225,74 @@ export function DesignCanvas({
1116
1225
  (fetchedExternalSnapshot?.url === rawExternalPreviewUrl
1117
1226
  ? fetchedExternalSnapshot.html
1118
1227
  : undefined);
1119
- const requiresEditableExternalSnapshot =
1228
+ const editorChromeBridgeForCurrentState = useMemo(
1229
+ () =>
1230
+ buildEditorChromeBridgeScript({
1231
+ readOnly,
1232
+ editMode,
1233
+ editorChromeScaleX: effectiveEditorChromeScaleX,
1234
+ editorChromeScaleY: effectiveEditorChromeScaleY,
1235
+ screenId: screenId ?? contentKey ?? "",
1236
+ boardSurface,
1237
+ }),
1238
+ [
1239
+ boardSurface,
1240
+ contentKey,
1241
+ editMode,
1242
+ effectiveEditorChromeScaleX,
1243
+ effectiveEditorChromeScaleY,
1244
+ readOnly,
1245
+ screenId,
1246
+ ],
1247
+ );
1248
+ const embeddedWheelBridgeForCurrentState = useMemo(
1249
+ () =>
1250
+ EMBEDDED_WHEEL_BRIDGE_SCRIPT.replace(
1251
+ "__EMBEDDED_WHEEL_FORWARDING_ENABLED__",
1252
+ isEmbeddedFrame ? "true" : "false",
1253
+ ),
1254
+ [isEmbeddedFrame],
1255
+ );
1256
+ const liveEditBridgeScript = useMemo(
1257
+ () =>
1258
+ MOTION_PREVIEW_BRIDGE_SCRIPT +
1259
+ SHADER_FILL_PREVIEW_BRIDGE_SCRIPT +
1260
+ TWEAK_BRIDGE_SCRIPT +
1261
+ ZOOM_BRIDGE_SCRIPT +
1262
+ LIGHTWEIGHT_HIT_TEST_BRIDGE_SCRIPT +
1263
+ embeddedWheelBridgeForCurrentState +
1264
+ editorChromeBridgeForCurrentState,
1265
+ [editorChromeBridgeForCurrentState, embeddedWheelBridgeForCurrentState],
1266
+ );
1267
+ const liveEditBridgeKey = useMemo(
1268
+ () => contentHash(liveEditBridgeScript),
1269
+ [liveEditBridgeScript],
1270
+ );
1271
+ const usesLiveEditExternalFrame =
1120
1272
  sourceType === "localhost" &&
1121
1273
  Boolean(bridgeUrl && rawExternalPreviewUrl) &&
1122
1274
  !interactMode &&
1123
1275
  !readOnly;
1276
+ const liveEditBridgeRegistered =
1277
+ usesLiveEditExternalFrame &&
1278
+ registeredLiveEditBridgeKey === liveEditBridgeKey;
1279
+ const requiresEditableExternalSnapshot = false;
1280
+ const liveEditExternalPreviewUrl =
1281
+ liveEditBridgeRegistered && bridgeUrl && rawExternalPreviewUrl
1282
+ ? liveEditEndpointUrl(bridgeUrl, rawExternalPreviewUrl)
1283
+ : null;
1124
1284
  const iframeRenderContent =
1125
1285
  activeExternalSnapshotHtml ??
1126
1286
  (requiresEditableExternalSnapshot ? "" : renderedContent);
1127
1287
  const externalPreviewUrl =
1128
- activeExternalSnapshotHtml || requiresEditableExternalSnapshot
1288
+ liveEditExternalPreviewUrl ??
1289
+ (activeExternalSnapshotHtml || requiresEditableExternalSnapshot
1129
1290
  ? null
1130
- : rawExternalPreviewUrl;
1291
+ : rawExternalPreviewUrl);
1131
1292
  const waitingForEditableExternalSnapshot =
1132
1293
  requiresEditableExternalSnapshot && !activeExternalSnapshotHtml;
1294
+ const waitingForLiveEditBridge =
1295
+ usesLiveEditExternalFrame && !liveEditBridgeRegistered;
1133
1296
  zoomRef.current = zoom;
1134
1297
  runtimeReplacementContentRef.current = runtimeReplacementContent;
1135
1298
  runtimeReplacementKeyRef.current = runtimeReplacementKey;
@@ -1138,9 +1301,61 @@ export function DesignCanvas({
1138
1301
  onExternalContentSnapshotRef.current = onExternalContentSnapshot;
1139
1302
  }, [onExternalContentSnapshot]);
1140
1303
 
1304
+ useEffect(() => {
1305
+ if (!usesLiveEditExternalFrame || !bridgeUrl) {
1306
+ setRegisteredLiveEditBridgeKey(null);
1307
+ return;
1308
+ }
1309
+ let cancelled = false;
1310
+ setRegisteredLiveEditBridgeKey((current) =>
1311
+ current === liveEditBridgeKey ? current : null,
1312
+ );
1313
+ void (async () => {
1314
+ try {
1315
+ const endpoint = new URL("/live-edit-bridge", bridgeUrl).toString();
1316
+ const response = await fetch(endpoint, {
1317
+ method: "POST",
1318
+ headers: {
1319
+ "content-type": "application/json",
1320
+ ...(bridgeToken ? { "x-bridge-token": bridgeToken } : {}),
1321
+ },
1322
+ body: JSON.stringify({ script: liveEditBridgeScript }),
1323
+ });
1324
+ if (!response.ok) {
1325
+ throw new Error(`Bridge registration failed (${response.status})`);
1326
+ }
1327
+ if (!cancelled) {
1328
+ setRegisteredLiveEditBridgeKey(liveEditBridgeKey);
1329
+ }
1330
+ } catch (error) {
1331
+ if (!cancelled) {
1332
+ console.warn(
1333
+ "[DesignCanvas] live edit bridge registration failed",
1334
+ error,
1335
+ );
1336
+ setRegisteredLiveEditBridgeKey(null);
1337
+ }
1338
+ }
1339
+ })();
1340
+ return () => {
1341
+ cancelled = true;
1342
+ };
1343
+ }, [
1344
+ bridgeUrl,
1345
+ bridgeToken,
1346
+ liveEditBridgeKey,
1347
+ liveEditBridgeScript,
1348
+ usesLiveEditExternalFrame,
1349
+ ]);
1350
+
1141
1351
  useEffect(() => {
1142
1352
  const previewUrl = rawExternalPreviewUrl;
1143
- if (sourceType !== "localhost" || !bridgeUrl || !previewUrl) {
1353
+ if (
1354
+ !requiresEditableExternalSnapshot ||
1355
+ sourceType !== "localhost" ||
1356
+ !bridgeUrl ||
1357
+ !previewUrl
1358
+ ) {
1144
1359
  snapshotRetryAttemptRef.current = 0;
1145
1360
  setFetchedExternalSnapshot((current) =>
1146
1361
  current?.url === previewUrl ? current : null,
@@ -1468,6 +1683,12 @@ export function DesignCanvas({
1468
1683
  origin: e.origin,
1469
1684
  iframeWindow,
1470
1685
  parentOrigin: window.location.origin,
1686
+ allowedOrigins:
1687
+ sourceType === "localhost" && bridgeUrl
1688
+ ? [originFromUrl(bridgeUrl)].filter(
1689
+ (origin): origin is string => Boolean(origin),
1690
+ )
1691
+ : [],
1471
1692
  });
1472
1693
  if (!trusted) {
1473
1694
  return;
@@ -1509,8 +1730,19 @@ export function DesignCanvas({
1509
1730
  e.data.styles && typeof e.data.styles === "object"
1510
1731
  ? (e.data.styles as Record<string, string>)
1511
1732
  : {};
1733
+ const originalStyles =
1734
+ e.data.originalStyles && typeof e.data.originalStyles === "object"
1735
+ ? (e.data.originalStyles as Record<string, string>)
1736
+ : undefined;
1512
1737
  if (selector && Object.keys(styles).length > 0) {
1513
- onVisualStyleChange?.(selector, styles, e.data.payload);
1738
+ onVisualStyleChange?.(
1739
+ selector,
1740
+ styles,
1741
+ isElementInfoPayload(e.data.payload) ? e.data.payload : undefined,
1742
+ {
1743
+ originalStyles,
1744
+ },
1745
+ );
1514
1746
  }
1515
1747
  return;
1516
1748
  }
@@ -1528,8 +1760,20 @@ export function DesignCanvas({
1528
1760
  const value = String(e.data.value ?? "");
1529
1761
  const html =
1530
1762
  typeof e.data.html === "string" ? String(e.data.html) : undefined;
1763
+ const originalValue =
1764
+ typeof e.data.originalValue === "string"
1765
+ ? String(e.data.originalValue)
1766
+ : undefined;
1767
+ const originalHtml =
1768
+ typeof e.data.originalHtml === "string"
1769
+ ? String(e.data.originalHtml)
1770
+ : undefined;
1531
1771
  if (selector) {
1532
- onTextContentChange?.(selector, value, e.data.payload, { html });
1772
+ onTextContentChange?.(selector, value, e.data.payload, {
1773
+ html,
1774
+ originalValue,
1775
+ originalHtml,
1776
+ });
1533
1777
  }
1534
1778
  return;
1535
1779
  }
@@ -1599,7 +1843,7 @@ export function DesignCanvas({
1599
1843
  anchorRect,
1600
1844
  },
1601
1845
  );
1602
- if (requestId) {
1846
+ if (requestId && applied !== "pending") {
1603
1847
  iframeRef.current?.contentWindow?.postMessage(
1604
1848
  {
1605
1849
  type: "visual-structure-ack",
@@ -1828,6 +2072,7 @@ export function DesignCanvas({
1828
2072
  onComponentSourceJump,
1829
2073
  isEmbeddedFrame,
1830
2074
  sourceType,
2075
+ bridgeUrl,
1831
2076
  fusionUrl,
1832
2077
  flushPendingOneShotMessages,
1833
2078
  ]);
@@ -2159,6 +2404,89 @@ export function DesignCanvas({
2159
2404
  [postOneShotBridgeMessage],
2160
2405
  );
2161
2406
 
2407
+ const lastStyleRevertRequestIdRef = useRef<number | null>(null);
2408
+ useEffect(() => {
2409
+ if (!styleRevertRequest) return;
2410
+ if (lastStyleRevertRequestIdRef.current === styleRevertRequest.requestId) {
2411
+ return;
2412
+ }
2413
+ lastStyleRevertRequestIdRef.current = styleRevertRequest.requestId;
2414
+ for (const patch of styleRevertRequest.patches) {
2415
+ const selectorCandidates = [
2416
+ patch.selector,
2417
+ patch.sourceId
2418
+ ? `[data-agent-native-node-id="${String(patch.sourceId).replace(/"/g, '\\"')}"]`
2419
+ : "",
2420
+ ].filter(Boolean);
2421
+ for (const [property, value] of Object.entries(patch.styles)) {
2422
+ postOneShotBridgeMessage({
2423
+ type: "style-change",
2424
+ selector: patch.selector,
2425
+ property,
2426
+ value,
2427
+ selectorCandidates,
2428
+ nodeId: patch.sourceId ?? "",
2429
+ });
2430
+ }
2431
+ }
2432
+ }, [postOneShotBridgeMessage, styleRevertRequest]);
2433
+
2434
+ const lastStyleBaselineResetRequestRef = useRef<number | null>(null);
2435
+ useEffect(() => {
2436
+ if (styleBaselineResetRequest == null) return;
2437
+ if (
2438
+ lastStyleBaselineResetRequestRef.current === styleBaselineResetRequest
2439
+ ) {
2440
+ return;
2441
+ }
2442
+ lastStyleBaselineResetRequestRef.current = styleBaselineResetRequest;
2443
+ postOneShotBridgeMessage({
2444
+ type: "agent-native:reset-live-visual-edit-baselines",
2445
+ });
2446
+ }, [postOneShotBridgeMessage, styleBaselineResetRequest]);
2447
+
2448
+ const lastTextRevertRequestIdRef = useRef<number | null>(null);
2449
+ useEffect(() => {
2450
+ if (!textRevertRequest) return;
2451
+ if (lastTextRevertRequestIdRef.current === textRevertRequest.requestId) {
2452
+ return;
2453
+ }
2454
+ lastTextRevertRequestIdRef.current = textRevertRequest.requestId;
2455
+ for (const patch of textRevertRequest.patches) {
2456
+ const selectorCandidates = [
2457
+ patch.selector,
2458
+ patch.sourceId
2459
+ ? `[data-agent-native-node-id="${String(patch.sourceId).replace(/"/g, '\\"')}"]`
2460
+ : "",
2461
+ ].filter(Boolean);
2462
+ postOneShotBridgeMessage({
2463
+ type: "set-text-content",
2464
+ selector: patch.selector,
2465
+ selectorCandidates,
2466
+ value: patch.value,
2467
+ html: patch.html,
2468
+ });
2469
+ }
2470
+ }, [postOneShotBridgeMessage, textRevertRequest]);
2471
+
2472
+ const lastStructureAckRequestIdRef = useRef<number | null>(null);
2473
+ useEffect(() => {
2474
+ if (!structureAckRequest) return;
2475
+ if (
2476
+ lastStructureAckRequestIdRef.current === structureAckRequest.requestId
2477
+ ) {
2478
+ return;
2479
+ }
2480
+ lastStructureAckRequestIdRef.current = structureAckRequest.requestId;
2481
+ for (const ack of structureAckRequest.acks) {
2482
+ postOneShotBridgeMessage({
2483
+ type: "visual-structure-ack",
2484
+ requestId: ack.requestId,
2485
+ applied: ack.applied,
2486
+ });
2487
+ }
2488
+ }, [postOneShotBridgeMessage, structureAckRequest]);
2489
+
2162
2490
  /**
2163
2491
  * Send a motion-preview scrub tick to the iframe. `t` is the normalised
2164
2492
  * playhead position in [0, 1]. Tracks must have been loaded first via the
@@ -2675,9 +3003,15 @@ export function DesignCanvas({
2675
3003
  </div>
2676
3004
  </div>
2677
3005
  ) : null}
2678
- {waitingForEditableExternalSnapshot ? (
3006
+ {waitingForEditableExternalSnapshot || waitingForLiveEditBridge ? (
2679
3007
  <div className="pointer-events-none absolute inset-0 z-10 flex items-center justify-center bg-background/85 px-4 text-center text-sm text-muted-foreground">
2680
- {externalSnapshotState?.status === "error" ? (
3008
+ {waitingForLiveEditBridge ? (
3009
+ <div className="max-w-[28rem] rounded-md border bg-card px-4 py-3 shadow-sm">
3010
+ {
3011
+ "Preparing live editor..." /* i18n-ignore transient localhost live-edit bridge loading state */
3012
+ }
3013
+ </div>
3014
+ ) : externalSnapshotState?.status === "error" ? (
2681
3015
  // A real offline dev server previously looked identical to the
2682
3016
  // ordinary "still loading" state and retried forever on a fixed
2683
3017
  // 1.5s timer — see getSnapshotRetryDelayMs for the backoff that
@@ -604,6 +604,32 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
604
604
  return styles;
605
605
  }
606
606
 
607
+ var liveVisualEditOriginalInlineStyles =
608
+ typeof WeakMap !== "undefined"
609
+ ? new WeakMap<Element, Record<string, string>>()
610
+ : null;
611
+
612
+ function rememberLiveVisualEditOriginalStyles(el: Element | null): void {
613
+ if (!el || !liveVisualEditOriginalInlineStyles) return;
614
+ if (liveVisualEditOriginalInlineStyles.has(el)) return;
615
+ liveVisualEditOriginalInlineStyles.set(el, collectInlineStyles(el));
616
+ }
617
+
618
+ function originalInlineStylesForPatch(
619
+ el: Element | null,
620
+ styles: Record<string, string>,
621
+ ): Record<string, string> {
622
+ if (!el || !liveVisualEditOriginalInlineStyles) return {};
623
+ rememberLiveVisualEditOriginalStyles(el);
624
+ var original = liveVisualEditOriginalInlineStyles.get(el) || {};
625
+ var patch: Record<string, string> = {};
626
+ Object.keys(styles).forEach(function (property) {
627
+ patch[property] =
628
+ typeof original[property] === "string" ? original[property] : "";
629
+ });
630
+ return patch;
631
+ }
632
+
607
633
  function chromeColorForElement(el: Element | null): string {
608
634
  return elementLooksLikeComponent(el)
609
635
  ? "var(--design-editor-component-color)"
@@ -946,6 +972,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
946
972
  }
947
973
 
948
974
  function postElementSelect(el: Element, e?: MouseEvent): void {
975
+ rememberLiveVisualEditOriginalStyles(el);
949
976
  var message: {
950
977
  type: string;
951
978
  payload: unknown;
@@ -4455,6 +4482,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4455
4482
  type: "visual-style-change",
4456
4483
  selector: getSelector(selectedEl),
4457
4484
  styles: styles,
4485
+ originalStyles: originalInlineStylesForPatch(selectedEl, styles),
4458
4486
  payload: getElementInfo(selectedEl),
4459
4487
  },
4460
4488
  "*",
@@ -4647,13 +4675,17 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4647
4675
  setActiveDragCancel(cancelSpacingDrag);
4648
4676
  }
4649
4677
 
4650
- function postTextContentChange(el, value, html) {
4678
+ function postTextContentChange(el, value, html, originalValue, originalHtml) {
4651
4679
  (window.parent as Window).postMessage(
4652
4680
  {
4653
4681
  type: "text-content-change",
4654
4682
  selector: getSelector(el),
4655
4683
  value: value,
4656
4684
  html: html,
4685
+ originalValue:
4686
+ typeof originalValue === "string" ? originalValue : undefined,
4687
+ originalHtml:
4688
+ typeof originalHtml === "string" ? originalHtml : undefined,
4657
4689
  payload: getElementInfo(el),
4658
4690
  },
4659
4691
  "*",
@@ -5126,15 +5158,17 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5126
5158
  el.style.display = "flex";
5127
5159
  el.style.flexDirection = inferred.direction;
5128
5160
  el.style.gap = inferred.gap + "px";
5161
+ var styles = {
5162
+ display: "flex",
5163
+ "flex-direction": inferred.direction,
5164
+ gap: inferred.gap + "px",
5165
+ };
5129
5166
  (window.parent as Window).postMessage(
5130
5167
  {
5131
5168
  type: "visual-style-change",
5132
5169
  selector: getSelector(container),
5133
- styles: {
5134
- display: "flex",
5135
- "flex-direction": inferred.direction,
5136
- gap: inferred.gap + "px",
5137
- },
5170
+ styles: styles,
5171
+ originalStyles: originalInlineStylesForPatch(container, styles),
5138
5172
  payload: getElementInfo(container),
5139
5173
  },
5140
5174
  "*",
@@ -5223,11 +5257,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5223
5257
  if (!containerBackgroundIsLight(container)) return;
5224
5258
  }
5225
5259
  el.style.color = "inherit";
5260
+ var styles = { color: "inherit" };
5226
5261
  (window.parent as Window).postMessage(
5227
5262
  {
5228
5263
  type: "visual-style-change",
5229
5264
  selector: getSelector(member),
5230
- styles: { color: "inherit" },
5265
+ styles: styles,
5266
+ originalStyles: originalInlineStylesForPatch(member, styles),
5231
5267
  payload: getElementInfo(member),
5232
5268
  },
5233
5269
  "*",
@@ -6590,15 +6626,17 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
6590
6626
  // order — the host composes them against its synchronous same-tick
6591
6627
  // content refs exactly like multi-property style commits.
6592
6628
  memberStates.forEach(function (state) {
6629
+ var styles = {
6630
+ position: state.el.style.position,
6631
+ left: state.el.style.left,
6632
+ top: state.el.style.top,
6633
+ };
6593
6634
  (window.parent as Window).postMessage(
6594
6635
  {
6595
6636
  type: "visual-style-change",
6596
6637
  selector: getSelector(state.el),
6597
- styles: {
6598
- position: state.el.style.position,
6599
- left: state.el.style.left,
6600
- top: state.el.style.top,
6601
- },
6638
+ styles: styles,
6639
+ originalStyles: originalInlineStylesForPatch(state.el, styles),
6602
6640
  payload: getElementInfo(state.el),
6603
6641
  },
6604
6642
  "*",
@@ -6832,6 +6870,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
6832
6870
  type: "visual-style-change",
6833
6871
  selector: getSelector(resizeEl),
6834
6872
  styles: styles,
6873
+ originalStyles: originalInlineStylesForPatch(resizeEl, styles),
6835
6874
  payload: getElementInfo(resizeEl),
6836
6875
  },
6837
6876
  "*",
@@ -6902,11 +6941,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
6902
6941
  cleanupRotateDrag();
6903
6942
  hideTransformBadge();
6904
6943
  if (!rotateEl) return;
6944
+ var styles = { transform: rotateEl.style.transform };
6905
6945
  (window.parent as Window).postMessage(
6906
6946
  {
6907
6947
  type: "visual-style-change",
6908
6948
  selector: getSelector(rotateEl),
6909
- styles: { transform: rotateEl.style.transform },
6949
+ styles: styles,
6950
+ originalStyles: originalInlineStylesForPatch(rotateEl, styles),
6910
6951
  payload: getElementInfo(rotateEl),
6911
6952
  },
6912
6953
  "*",
@@ -7461,7 +7502,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
7461
7502
  var nextHtml = target.innerHTML || "";
7462
7503
  refreshOverlays();
7463
7504
  if (next !== originalText || nextHtml !== originalHtml) {
7464
- postTextContentChange(target, next, nextHtml);
7505
+ postTextContentChange(
7506
+ target,
7507
+ next,
7508
+ nextHtml,
7509
+ originalText,
7510
+ originalHtml,
7511
+ );
7465
7512
  }
7466
7513
  // T13: replay the latest runtime-content update that arrived (and was
7467
7514
  // buffered) while this edit session was active, so the canvas isn't
@@ -7905,6 +7952,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
7905
7952
  cancelActiveBridgeDrag();
7906
7953
  return;
7907
7954
  }
7955
+ if (e.data.type === "agent-native:reset-live-visual-edit-baselines") {
7956
+ if (liveVisualEditOriginalInlineStyles) {
7957
+ liveVisualEditOriginalInlineStyles = new WeakMap<
7958
+ Element,
7959
+ Record<string, string>
7960
+ >();
7961
+ }
7962
+ return;
7963
+ }
7908
7964
  if (e.data.type === "clear-selection") {
7909
7965
  // During marquee drag, empty hit sets are replayed back from the host as a
7910
7966
  // clear-selection state. Keep the drag-owned rectangle alive until pointer-up.
@@ -8168,6 +8224,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
8168
8224
  removeRuntimeTarget(e.data.selector, e.data.selectorCandidates);
8169
8225
  return;
8170
8226
  }
8227
+ if (e.data.type === "set-text-content") {
8228
+ var textTarget = findRuntimeTarget(
8229
+ String(e.data.selector || ""),
8230
+ Array.isArray(e.data.selectorCandidates)
8231
+ ? e.data.selectorCandidates
8232
+ : [],
8233
+ ) as HTMLElement | null;
8234
+ if (!textTarget) return;
8235
+ if (typeof e.data.html === "string") {
8236
+ textTarget.innerHTML = e.data.html;
8237
+ } else {
8238
+ textTarget.textContent =
8239
+ typeof e.data.value === "string" ? e.data.value : "";
8240
+ }
8241
+ refreshOverlays();
8242
+ return;
8243
+ }
8171
8244
  if (e.data.type !== "style-change") return;
8172
8245
  var sel = e.data.selector;
8173
8246
  var prop = e.data.property;
@@ -8211,6 +8284,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
8211
8284
  activeTextEditEl,
8212
8285
  activeTextEditEl!.textContent || "",
8213
8286
  activeTextEditEl!.innerHTML || "",
8287
+ undefined,
8288
+ undefined,
8214
8289
  );
8215
8290
  refreshOverlays();
8216
8291
  return;