nudge-ui 0.2.2 → 0.2.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 (244) hide show
  1. package/README.md +164 -46
  2. package/dist/client.mjs +260 -61
  3. package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
  4. package/dist/css/value-semantics/colorSemantics.js +17 -0
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
  6. package/dist/hosts/astro/clientTransport.js +38 -32
  7. package/dist/hosts/astro/integration.js +5 -1
  8. package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
  9. package/dist/hosts/astro/markdownIdentity.js +81 -0
  10. package/dist/hosts/next/manifest.js +1 -1
  11. package/dist/hosts/next/sidecar.js +5 -1
  12. package/dist/hosts/static/manifest.js +1 -1
  13. package/dist/hosts/static/server.js +3 -0
  14. package/dist/hosts/vite/vite.d.ts +3 -0
  15. package/dist/hosts/vite/vite.js +24 -4
  16. package/dist/inspector/agent/McpConnectionDialog.js +13 -19
  17. package/dist/inspector/agent/activity.d.ts +18 -0
  18. package/dist/inspector/agent/activity.js +70 -0
  19. package/dist/inspector/agent/client.d.ts +2 -0
  20. package/dist/inspector/agent/client.js +29 -1
  21. package/dist/inspector/agent/connectionStatus.js +1 -1
  22. package/dist/inspector/agent/httpTransport.js +9 -2
  23. package/dist/inspector/agent/protocol.d.ts +6 -3
  24. package/dist/inspector/agent/protocol.js +1 -1
  25. package/dist/inspector/agent/verification.d.ts +15 -7
  26. package/dist/inspector/agent/verification.js +113 -41
  27. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
  28. package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
  29. package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
  30. package/dist/inspector/canvas/CanvasCard.js +135 -38
  31. package/dist/inspector/canvas/CanvasElementOverlay.js +166 -16
  32. package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
  33. package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
  34. package/dist/inspector/canvas/CanvasToolbar.js +8 -4
  35. package/dist/inspector/canvas/CanvasWorkspace.js +270 -122
  36. package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
  37. package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
  38. package/dist/inspector/canvas/agentPresentation.js +64 -52
  39. package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
  40. package/dist/inspector/canvas/canvasGestures.js +1 -1
  41. package/dist/inspector/canvas/canvasStore.d.ts +46 -82
  42. package/dist/inspector/canvas/canvasStore.js +251 -284
  43. package/dist/inspector/canvas/frameContent.d.ts +21 -0
  44. package/dist/inspector/canvas/frameContent.js +44 -0
  45. package/dist/inspector/canvas/frameProtocol.d.ts +21 -4
  46. package/dist/inspector/canvas/frameProtocol.js +3 -3
  47. package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
  48. package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
  49. package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -1
  50. package/dist/inspector/canvas/keyboardShortcuts.js +4 -4
  51. package/dist/inspector/canvas/layout.d.ts +52 -0
  52. package/dist/inspector/canvas/layout.js +106 -0
  53. package/dist/inspector/canvas/projection.d.ts +15 -5
  54. package/dist/inspector/canvas/projection.js +69 -13
  55. package/dist/inspector/canvas/rendererBootstrap.js +32 -4
  56. package/dist/inspector/canvas/rendererElementSelector.js +47 -6
  57. package/dist/inspector/canvas/rendererStylesheet.js +2 -2
  58. package/dist/inspector/canvas/resizeHandleScale.js +1 -1
  59. package/dist/inspector/canvas/sessionStore.d.ts +11 -26
  60. package/dist/inspector/canvas/sessionStore.js +43 -183
  61. package/dist/inspector/canvas/staleChangeDetector.js +9 -3
  62. package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
  63. package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
  64. package/dist/inspector/canvas/viewStore.d.ts +38 -0
  65. package/dist/inspector/canvas/viewStore.js +104 -0
  66. package/dist/inspector/canvas/wheelPan.d.ts +8 -0
  67. package/dist/inspector/canvas/wheelPan.js +8 -0
  68. package/dist/inspector/changes/changesLog.d.ts +9 -6
  69. package/dist/inspector/changes/changesLog.js +48 -32
  70. package/dist/inspector/changes/codecs.d.ts +1 -0
  71. package/dist/inspector/changes/codecs.js +10 -1
  72. package/dist/inspector/changes/draftChanges.d.ts +58 -0
  73. package/dist/inspector/changes/draftChanges.js +304 -0
  74. package/dist/inspector/changes/editModel.d.ts +10 -0
  75. package/dist/inspector/changes/editModel.js +9 -2
  76. package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
  77. package/dist/inspector/changes/previewDiagnostics.js +11 -25
  78. package/dist/inspector/changes/types.d.ts +7 -0
  79. package/dist/inspector/clientManifest.d.ts +1 -1
  80. package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
  81. package/dist/inspector/comments/CommentsOverlay.js +250 -0
  82. package/dist/inspector/comments/element.d.ts +6 -0
  83. package/dist/inspector/comments/element.js +54 -0
  84. package/dist/inspector/comments/store.d.ts +29 -0
  85. package/dist/inspector/comments/store.js +121 -0
  86. package/dist/inspector/comments/verification.d.ts +5 -0
  87. package/dist/inspector/comments/verification.js +52 -0
  88. package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
  89. package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
  90. package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
  91. package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
  92. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
  93. package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
  94. package/dist/inspector/componentSemantics/types.d.ts +11 -0
  95. package/dist/inspector/drafts/model.d.ts +35 -0
  96. package/dist/inspector/drafts/model.js +45 -0
  97. package/dist/inspector/drafts/store.d.ts +28 -0
  98. package/dist/inspector/drafts/store.js +231 -0
  99. package/dist/inspector/index.js +20 -27
  100. package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
  101. package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
  102. package/dist/inspector/iterations/capture.d.ts +2 -0
  103. package/dist/inspector/iterations/capture.js +174 -0
  104. package/dist/inspector/iterations/client.d.ts +7 -0
  105. package/dist/inspector/iterations/client.js +52 -0
  106. package/dist/inspector/iterations/coordinator.d.ts +15 -0
  107. package/dist/inspector/iterations/coordinator.js +130 -0
  108. package/dist/inspector/iterations/prompt.d.ts +3 -0
  109. package/dist/inspector/iterations/prompt.js +26 -0
  110. package/dist/inspector/overlay/DomNavigator.js +21 -15
  111. package/dist/inspector/overlay/interactionStyles.js +3 -1
  112. package/dist/inspector/overlay/spacingGestures.d.ts +13 -1
  113. package/dist/inspector/overlay/spacingGestures.js +82 -5
  114. package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
  115. package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
  116. package/dist/inspector/projection/renderedInstance.d.ts +17 -3
  117. package/dist/inspector/projection/renderedInstance.js +62 -11
  118. package/dist/inspector/projection/structuralProjection.d.ts +2 -1
  119. package/dist/inspector/projection/structuralProjection.js +21 -17
  120. package/dist/inspector/projection/textProjection.d.ts +2 -1
  121. package/dist/inspector/projection/textProjection.js +64 -15
  122. package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
  123. package/dist/inspector/prompt/clipboardHandoff.d.ts +15 -18
  124. package/dist/inspector/prompt/clipboardHandoff.js +97 -143
  125. package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
  126. package/dist/inspector/prompt/generatePrompt.js +22 -17
  127. package/dist/inspector/runtime/runtimeConfig.d.ts +9 -0
  128. package/dist/inspector/runtime/runtimeConfig.js +24 -0
  129. package/dist/inspector/selection/sourceSite.d.ts +12 -0
  130. package/dist/inspector/selection/sourceSite.js +28 -0
  131. package/dist/inspector/settings/SettingsDialog.js +21 -8
  132. package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
  133. package/dist/inspector/shell/AgentStatusButton.js +36 -0
  134. package/dist/inspector/shell/AppShell.js +4 -2
  135. package/dist/inspector/shell/ChangesLog.js +46 -26
  136. package/dist/inspector/shell/CopyPromptButton.js +184 -93
  137. package/dist/inspector/shell/EmptyState.js +9 -8
  138. package/dist/inspector/shell/InspectorShell.js +13 -21
  139. package/dist/inspector/shell/PeekOriginalButton.js +24 -23
  140. package/dist/inspector/shell/originalPreview.js +5 -5
  141. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  142. package/dist/inspector/shell/shortcuts.js +11 -0
  143. package/dist/inspector/sketch/SketchEditor.js +14 -14
  144. package/dist/inspector/sketch/SketchEntryButton.js +1 -1
  145. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
  146. package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
  147. package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
  148. package/dist/inspector/sketch/SketchOverlay.js +7 -7
  149. package/dist/inspector/sketch/SketchWorkspace.js +18 -12
  150. package/dist/inspector/sketch/interaction.d.ts +1 -0
  151. package/dist/inspector/sketch/interaction.js +8 -4
  152. package/dist/inspector/sketch/model.d.ts +5 -0
  153. package/dist/inspector/sketch/model.js +6 -2
  154. package/dist/inspector/sketch/raster.d.ts +1 -2
  155. package/dist/inspector/sketch/raster.js +68 -4
  156. package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
  157. package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
  158. package/dist/inspector/styleEditors/BorderEditor.js +49 -78
  159. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
  160. package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
  161. package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
  162. package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
  163. package/dist/inspector/styleEditors/ColorPicker.js +10 -4
  164. package/dist/inspector/styleEditors/GapField.js +3 -5
  165. package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
  166. package/dist/inspector/styleEditors/GridChildSection.js +18 -9
  167. package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
  168. package/dist/inspector/styleEditors/GridPicker.js +8 -2
  169. package/dist/inspector/styleEditors/GridSection.js +1 -1
  170. package/dist/inspector/styleEditors/GridValueField.js +19 -14
  171. package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
  172. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
  173. package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
  174. package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
  175. package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
  176. package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
  177. package/dist/inspector/styleEditors/LayoutSection.js +32 -11
  178. package/dist/inspector/styleEditors/SpacingBox.js +7 -5
  179. package/dist/inspector/styleEditors/Typography.js +11 -3
  180. package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
  181. package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
  182. package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
  183. package/dist/inspector/styleEditors/gradientValue.js +114 -0
  184. package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
  185. package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
  186. package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
  187. package/dist/inspector/styleEditors/layoutValue.js +0 -42
  188. package/dist/inspector/styleEditors/nudgeValue.d.ts +2 -2
  189. package/dist/inspector/styleEditors/nudgeValue.js +36 -8
  190. package/dist/inspector/tokens/TokenField.d.ts +7 -0
  191. package/dist/inspector/tokens/TokenField.js +35 -25
  192. package/dist/inspector/tokens/TokenLabel.js +5 -14
  193. package/dist/inspector/tokens/editActions.d.ts +1 -1
  194. package/dist/inspector/tokens/editActions.js +7 -1
  195. package/dist/inspector/ui/AtRuleContext.js +4 -6
  196. package/dist/inspector/ui/Button.d.ts +6 -2
  197. package/dist/inspector/ui/Button.js +4 -3
  198. package/dist/inspector/ui/ControlSurface.d.ts +2 -2
  199. package/dist/inspector/ui/ControlSurface.js +4 -3
  200. package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
  201. package/dist/inspector/ui/InlineStyleWarning.js +13 -0
  202. package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
  203. package/dist/inspector/ui/InspectorPopover.js +4 -5
  204. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  205. package/dist/inspector/ui/PopoverListbox.js +8 -7
  206. package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
  207. package/dist/inspector/ui/SegmentedControl.js +2 -2
  208. package/dist/inspector/ui/Select.d.ts +6 -1
  209. package/dist/inspector/ui/Select.js +7 -11
  210. package/dist/inspector/ui/SideValuesField.js +1 -1
  211. package/dist/inspector/ui/Tooltip.d.ts +26 -0
  212. package/dist/inspector/ui/Tooltip.js +32 -0
  213. package/dist/inspector/ui/classNames.d.ts +2 -0
  214. package/dist/inspector/ui/classNames.js +4 -0
  215. package/dist/inspector/ui/customCursors.d.ts +23 -0
  216. package/dist/inspector/ui/customCursors.js +122 -0
  217. package/dist/inspector/ui/dragCursor.d.ts +0 -2
  218. package/dist/inspector/ui/dragCursor.js +2 -10
  219. package/dist/inspector/ui/portalContainer.d.ts +2 -0
  220. package/dist/inspector/ui/portalContainer.js +6 -0
  221. package/dist/inspector/ui/styles.js +29 -21
  222. package/dist/inspector/workspace/commands.d.ts +9 -0
  223. package/dist/inspector/workspace/commands.js +82 -0
  224. package/dist/inspector/workspace/controller.d.ts +4 -0
  225. package/dist/inspector/workspace/controller.js +66 -0
  226. package/dist/inspector/workspace/handoff.d.ts +42 -0
  227. package/dist/inspector/workspace/handoff.js +115 -0
  228. package/dist/inspector/workspace/sentItems.d.ts +19 -0
  229. package/dist/inspector/workspace/sentItems.js +51 -0
  230. package/dist/inspector/workspace/timeline.d.ts +30 -0
  231. package/dist/inspector/workspace/timeline.js +108 -0
  232. package/dist/internal/agent-protocol.d.ts +19 -2
  233. package/dist/internal/agent-protocol.js +13 -0
  234. package/dist/nudge-ui.mjs +558 -311
  235. package/dist/project/artifacts.d.ts +13 -0
  236. package/dist/project/artifacts.js +253 -0
  237. package/dist/transport/artifacts.d.ts +8 -0
  238. package/dist/transport/artifacts.js +9 -0
  239. package/dist/transport/editor.js +3 -1
  240. package/dist/transport/routes.d.ts +2 -0
  241. package/dist/transport/routes.js +2 -0
  242. package/package.json +7 -3
  243. package/dist/inspector/changes/workspaceChanges.d.ts +0 -47
  244. package/dist/inspector/changes/workspaceChanges.js +0 -265
@@ -1,11 +1,17 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { AgentActivityOverlay } from "./AgentActivityOverlay.js";
3
+ import { contentDocumentUrl, contentNavigationUrl, contentSourceUrl, iterationId } from "./frameContent.js";
4
+ import { refreshHtmlIteration } from "../iterations/coordinator.js";
2
5
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
3
6
  import { CANVAS_RENDERER_ATTR } from "./roleDetection.js";
4
- import { removeCanvasCard, updateCardTitle, updateCardUrl, resizeCard, setCardPosition, selectCard, getSelectedCardId, useSelectedCardId, useFocusedCardId, useBoardCamera } from "./canvasStore.js";
5
- import { IconRefresh, IconArrowsDiagonal, IconCornerLeftDown } from "@tabler/icons-react";
7
+ import { useCanvasCards, getCanvasCardLabel, updateCardUrl, resizeCard, setCardPosition, selectCard, getSelectedCardId, useSelectedCardId, useFocusedCardId, } from "./canvasStore.js";
8
+ import { useBoardCamera } from "./viewStore.js";
9
+ import { IconArrowsDiagonal, IconCopy, IconCornerLeftDown, IconBoltFilled, IconArtboard, IconTrash } from "@tabler/icons-react";
6
10
  import { PROTOCOL_VERSION, } from "./frameProtocol.js";
7
11
  import { registerCardFrame, registerCardFrameSource, unregisterCardFrame, sendProjectionToCard, invalidateCanvasPreviewDocument, PROJECT_ID, WORKSPACE_ID } from "./projection.js";
8
12
  import { Button } from "../ui/Button.js";
13
+ import { IconButton } from "../ui/IconButton.js";
14
+ import { Tooltip } from "../ui/Tooltip.js";
9
15
  import { setSelectedElement } from "../selection/selectionStore.js";
10
16
  import { clearCanvasStructuralProjectionReports } from "../projection/structuralProjection.js";
11
17
  import { clearCanvasRenderedInstanceProjectionReports } from "../projection/renderedInstance.js";
@@ -15,12 +21,17 @@ import { getCanvasResizeHandleScale } from "./resizeHandleScale.js";
15
21
  import { disposeBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
16
22
  import { releaseDocumentProjection } from "../projection/structuralProjection.js";
17
23
  import { startClipboardHandoffController } from "../prompt/clipboardHandoff.js";
18
- import { disposeInlineTextEdit } from "../inline-text/inlineTextEditor.js";
24
+ import { disposeInlineTextEdit, isInlineTextEditingActive } from "../inline-text/inlineTextEditor.js";
19
25
  import { configureNudgeUiRuntime, getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
26
+ import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
20
27
  import { reconcileRuntimeWithDocumentStylesheets } from "../runtime/documentStylesheetOrder.js";
21
28
  import { subscribeCanvasRendererMessages } from "./rendererMessageRouter.js";
22
29
  import { SketchFrameOverlay } from "../sketch/SketchFrameOverlay.js";
30
+ import { installFrameScrollbarStyles } from "./frameScrollbarStyles.js";
23
31
  import { getCanvasResizeEdges, resizeCanvasRect, } from "./canvasResize.js";
32
+ import { getDraftContentsForCard } from "../drafts/store.js";
33
+ import { removeFrame } from "../workspace/commands.js";
34
+ import { artifactDocumentUrl } from "../iterations/client.js";
24
35
  const RESIZE_HANDLES = [
25
36
  { direction: "top-left", cursor: "nwse-resize", transformOrigin: "left top", label: "Resize card from the top-left corner" },
26
37
  { direction: "top", cursor: "ns-resize", transformOrigin: "center top", label: "Resize card from the top edge" },
@@ -38,15 +49,23 @@ function resizeHandleTransform(direction, scale) {
38
49
  return `scale(${scale}, 1)`;
39
50
  return `scale(${scale})`;
40
51
  }
41
- export function CanvasCard({ card, presentation = "canvas", presentationCard = true, onShowFocus, documentOwner }) {
52
+ export function CanvasCard({ card, presentation = "canvas", presentationCard = true, linkedToActive = false, onShowFocus, onDuplicate, onCreateIteration, documentOwner }) {
42
53
  const iframeRef = useRef(null);
43
- const initialUrlRef = useRef(card.navigationUrl ?? card.url);
44
- const navigationUrlRef = useRef(card.navigationUrl);
54
+ const initialUrlRef = useRef(contentNavigationUrl(card.content) ?? contentSourceUrl(card.content));
55
+ const navigationUrlRef = useRef(contentNavigationUrl(card.content));
45
56
  const documentSessionRef = useRef(null);
46
57
  const frameRuntimeRef = useRef(null);
47
58
  const [loadState, setLoadState] = useState("loading");
48
59
  const [errorMessage, setErrorMessage] = useState(null);
60
+ const [creatingIteration, setCreatingIteration] = useState(false);
61
+ const [iterationError, setIterationError] = useState(null);
49
62
  const camera = useBoardCamera();
63
+ const runtimeConfig = useNudgeUiRuntimeConfig();
64
+ const canvasCards = useCanvasCards();
65
+ const canRemove = canvasCards.length > 1;
66
+ const frameLabel = runtimeConfig.demo === true && runtimeConfig.demoCardLabels?.length && card.title
67
+ ? card.title
68
+ : getCanvasCardLabel(card, canvasCards);
50
69
  const selectedCardId = useSelectedCardId();
51
70
  const focusedCardId = useFocusedCardId();
52
71
  const isSelected = selectedCardId === card.id;
@@ -82,6 +101,7 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
82
101
  session.registerCleanup(() => releaseDocumentProjection(frameDocument));
83
102
  session.registerCleanup(() => disposeInlineTextEdit("frame-disposed", frameDocument));
84
103
  session.registerCleanup(startClipboardHandoffController(frameDocument));
104
+ session.registerCleanup(installFrameScrollbarStyles(frameDocument));
85
105
  documentSessionRef.current = { document: frameDocument, session };
86
106
  }, [disposeDocumentSession, documentOwner]);
87
107
  useEffect(() => {
@@ -94,22 +114,59 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
94
114
  disposeDocumentSession();
95
115
  unregisterCardFrame(card.id);
96
116
  };
97
- }, [bindDocumentSession, card.id, disposeDocumentSession]);
117
+ }, [bindDocumentSession, card.id, iterationId(card.content), disposeDocumentSession]);
118
+ useEffect(() => {
119
+ if (card.content.kind !== "iteration" || loadState !== "ready")
120
+ return;
121
+ const artifactId = card.content.artifactId;
122
+ let active = true;
123
+ let checking = false;
124
+ let reloadPending = false;
125
+ const timer = window.setInterval(() => {
126
+ const workspace = getDraftContentsForCard(card.id);
127
+ // Pending inspector edits still project over preview.html. Do not replace
128
+ // that base until they have been saved and handed off.
129
+ if (checking || isInlineTextEditingActive() || workspace?.changes.length || workspace?.structuralChanges.length)
130
+ return;
131
+ checking = true;
132
+ const frame = iframeRef.current;
133
+ if (!frame) {
134
+ checking = false;
135
+ return;
136
+ }
137
+ void refreshHtmlIteration(card.id, artifactId, frame).then((changed) => {
138
+ reloadPending ||= changed;
139
+ const latest = getDraftContentsForCard(card.id);
140
+ if (active && reloadPending && !isInlineTextEditingActive() && !latest?.changes.length && !latest?.structuralChanges.length) {
141
+ reloadPending = false;
142
+ loadFrame(`${artifactDocumentUrl(artifactId)}?revision=${Date.now()}`);
143
+ }
144
+ }).catch(() => undefined).finally(() => { checking = false; });
145
+ }, 1500);
146
+ return () => { active = false; window.clearInterval(timer); };
147
+ }, [iterationId(card.content), card.id, loadFrame, loadState]);
98
148
  function handleReload() {
99
149
  loadFrame();
100
150
  }
101
151
  function handleRemove() {
102
152
  if (getSelectedCardId() === card.id)
103
153
  setSelectedElement(null);
104
- removeCanvasCard(card.id);
154
+ removeFrame(card.id);
105
155
  }
106
- function updateFrameTitle(title) {
107
- if (!title)
108
- return;
109
- const runtime = getNudgeUiRuntimeConfig();
110
- if (runtime.demo === true && (runtime.demoCardLabels?.length ?? 0) > 0)
156
+ async function handleIteration(position) {
157
+ if (creatingIteration || !onCreateIteration || loadState !== "ready")
111
158
  return;
112
- updateCardTitle(card.id, title);
159
+ setCreatingIteration(true);
160
+ setIterationError(null);
161
+ try {
162
+ return await onCreateIteration(card.id, position);
163
+ }
164
+ catch (error) {
165
+ setIterationError(error instanceof Error ? error.message : "The iteration could not be created.");
166
+ }
167
+ finally {
168
+ setCreatingIteration(false);
169
+ }
113
170
  }
114
171
  /** The controller half of the handshake: announce workspace identity. */
115
172
  function sendParentReady() {
@@ -158,8 +215,7 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
158
215
  adoptFrameRuntime(data.runtime);
159
216
  setLoadState("ready");
160
217
  setErrorMessage(null);
161
- updateFrameTitle(data.title);
162
- if (data.url)
218
+ if (data.url && !iterationId(card.content))
163
219
  updateCardUrl(card.id, data.url);
164
220
  if (iframeRef.current) {
165
221
  registerCardFrame(card.id, iframeRef.current);
@@ -172,8 +228,7 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
172
228
  return;
173
229
  }
174
230
  if (data.type === "frame-metadata") {
175
- updateFrameTitle(data.title);
176
- if (data.url)
231
+ if (data.url && !iterationId(card.content))
177
232
  updateCardUrl(card.id, data.url);
178
233
  return;
179
234
  }
@@ -182,13 +237,13 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
182
237
  setErrorMessage(data.message || "Frame failed to load");
183
238
  }
184
239
  });
185
- }, [card.id]);
240
+ }, [card.id, iterationId(card.content)]);
186
241
  useLayoutEffect(() => {
187
- if (!card.navigationUrl || navigationUrlRef.current === card.navigationUrl)
242
+ if (!contentNavigationUrl(card.content) || navigationUrlRef.current === contentNavigationUrl(card.content))
188
243
  return;
189
- navigationUrlRef.current = card.navigationUrl;
190
- loadFrame(card.navigationUrl);
191
- }, [card.navigationUrl, loadFrame]);
244
+ navigationUrlRef.current = contentNavigationUrl(card.content);
245
+ loadFrame(contentNavigationUrl(card.content));
246
+ }, [contentNavigationUrl(card.content), loadFrame]);
192
247
  useEffect(() => {
193
248
  if (loadState !== "loading")
194
249
  return;
@@ -220,12 +275,13 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
220
275
  }
221
276
  iframe.addEventListener("load", onLoad);
222
277
  return () => iframe.removeEventListener("load", onLoad);
223
- }, [bindDocumentSession, card.id, disposeDocumentSession]);
278
+ }, [bindDocumentSession, card.id, iterationId(card.content), disposeDocumentSession]);
224
279
  const [isDragging, setIsDragging] = useState(false);
225
280
  const dragRef = useRef({ startX: 0, startY: 0, cardX: 0, cardY: 0 });
281
+ const dragCleanupRef = useRef(null);
226
282
  const resizeCleanupRef = useRef(null);
227
283
  useEffect(() => {
228
- return () => resizeCleanupRef.current?.();
284
+ return () => { resizeCleanupRef.current?.(); dragCleanupRef.current?.(); };
229
285
  }, []);
230
286
  const handleToolbarPointerDown = useCallback((e) => {
231
287
  // SAFETY: pointer events on the card target are HTMLElements in this DOM context.
@@ -237,6 +293,12 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
237
293
  selectCard(card.id);
238
294
  e.stopPropagation();
239
295
  e.preventDefault();
296
+ const iterationDrag = e.altKey && !!onCreateIteration && loadState === "ready" && !creatingIteration;
297
+ let moved = false;
298
+ let iterationId;
299
+ let creationStarted = false;
300
+ let cancelled = false;
301
+ let position = { x: card.x, y: card.y };
240
302
  dragRef.current = {
241
303
  startX: e.clientX,
242
304
  startY: e.clientY,
@@ -249,16 +311,56 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
249
311
  function onMove(ev) {
250
312
  const dx = (ev.clientX - dragRef.current.startX) / camera.zoom;
251
313
  const dy = (ev.clientY - dragRef.current.startY) / camera.zoom;
252
- setCardPosition(card.id, dragRef.current.cardX + dx, dragRef.current.cardY + dy);
314
+ moved ||= Math.hypot(ev.clientX - dragRef.current.startX, ev.clientY - dragRef.current.startY) >= 6;
315
+ position = { x: dragRef.current.cardX + dx, y: dragRef.current.cardY + dy };
316
+ if (iterationDrag) {
317
+ if (iterationId)
318
+ setCardPosition(iterationId, position.x, position.y);
319
+ else if (moved && !creationStarted) {
320
+ creationStarted = true;
321
+ void handleIteration(position).then((id) => {
322
+ if (!id)
323
+ return;
324
+ iterationId = id;
325
+ if (cancelled) {
326
+ removeFrame(id, { undoable: false });
327
+ selectCard(card.id);
328
+ }
329
+ else
330
+ setCardPosition(id, position.x, position.y);
331
+ });
332
+ }
333
+ }
334
+ else
335
+ setCardPosition(card.id, position.x, position.y);
253
336
  }
254
- function onUp() {
255
- setIsDragging(false);
337
+ function cleanup() {
256
338
  window.removeEventListener("pointermove", onMove);
257
339
  window.removeEventListener("pointerup", onUp);
340
+ window.removeEventListener("pointercancel", onCancel);
341
+ dragCleanupRef.current = null;
258
342
  }
343
+ function onCancel() {
344
+ cleanup();
345
+ setIsDragging(false);
346
+ cancelled = true;
347
+ if (iterationId) {
348
+ removeFrame(iterationId, { undoable: false });
349
+ selectCard(card.id);
350
+ }
351
+ }
352
+ function onUp() {
353
+ cleanup();
354
+ setIsDragging(false);
355
+ if (iterationId)
356
+ setCardPosition(iterationId, position.x, position.y);
357
+ }
358
+ dragCleanupRef.current?.();
359
+ dragCleanupRef.current = onCancel;
259
360
  window.addEventListener("pointermove", onMove);
260
361
  window.addEventListener("pointerup", onUp);
261
- }, [card.id, card.x, card.y, camera.zoom]);
362
+ window.addEventListener("pointercancel", onCancel);
363
+ }, [card.id, card.x, card.y, camera.zoom, onCreateIteration, loadState, creatingIteration]);
262
364
  const handleCardPointerDown = useCallback(() => {
263
365
  if (getSelectedCardId() !== card.id)
264
366
  setSelectedElement(null);
@@ -341,7 +443,8 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
341
443
  const next = resizeCanvasRect(card, direction, delta);
342
444
  resizeCard(card.id, next.width, next.height, { x: next.x, y: next.y });
343
445
  }
344
- return (_jsxs("div", { className: `canvas-card${isDragging ? " is-dragging" : ""}${isSelected ? " is-selected" : ""}${isFocused ? " is-focused" : ""}${isFocusPresentationCard ? " is-focus-presentation" : ""}${presentationCard ? " is-presentation-card" : ""}`, "data-card-id": card.id, style: {
446
+ return (_jsxs("div", { className: `canvas-card${linkedToActive ? " is-linked" : ""}${isDragging ? " is-dragging" : ""}${isSelected ? " is-selected" : ""}${isFocused ? " is-focused" : ""}${isFocusPresentationCard ? " is-focus-presentation" : ""}${presentationCard ? " is-presentation-card" : ""}`, "data-card-id": card.id, tabIndex: presentation === "canvas" ? 0 : undefined, role: "group", "aria-label": `Frame: ${frameLabel}`, style: {
447
+ "--canvas-toolbar-scale": toolbarScale,
345
448
  position: "absolute",
346
449
  visibility: presentation === "focus" && !presentationCard ? "hidden" : undefined,
347
450
  pointerEvents: presentation === "focus" && !presentationCard ? "none" : undefined,
@@ -349,13 +452,7 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
349
452
  top: isFocusPresentationCard ? 0 : card.y,
350
453
  width: isFocusPresentationCard ? "100%" : card.width,
351
454
  height: isFocusPresentationCard ? "100%" : card.height,
352
- }, onPointerDown: presentation === "canvas" ? handleCardPointerDown : undefined, children: [presentation === "canvas" ? _jsx("div", { className: "canvas-card__toolbar", onPointerDown: handleToolbarPointerDown, children: _jsxs("div", { className: "canvas-card__drag-surface", "data-test": `canvas-card-drag-${card.id}`, children: [onShowFocus ? (_jsxs(Button, { title: "Focus", variant: "primary", size: "default", "data-test": `canvas-card-focus-${card.id}`, style: {
353
- transform: `scale(${toolbarScale})`,
354
- transformOrigin: "left bottom",
355
- }, onClick: () => onShowFocus(card.id), children: [_jsx(IconCornerLeftDown, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), "Focus"] })) : null, _jsx("span", { className: "canvas-card__dimensions", "data-test": `canvas-card-dimensions-${card.id}`, style: {
356
- transform: `translateX(-50%) scale(${toolbarScale})`,
357
- transformOrigin: "center bottom",
358
- }, children: card.title || `${Math.round(card.width)} × ${Math.round(card.height)} px` })] }) }) : null, _jsxs("div", { className: "canvas-card__frame", children: [loadState === "loading" ? (_jsx("div", { className: "canvas-card__loading", "data-test": `canvas-card-loading-${card.id}`, children: "Loading preview..." })) : loadState === "error" ? (_jsxs("div", { className: "canvas-card__error", "data-test": `canvas-card-error-${card.id}`, children: [_jsx("p", { children: errorMessage || "Frame could not be loaded" }), _jsxs("div", { className: "canvas-card__error-actions", children: [_jsx(Button, { size: "compact", variant: "secondary", onClick: handleReload, children: "Retry" }), _jsx(Button, { size: "compact", variant: "danger", onClick: handleRemove, children: "Remove" })] })] })) : null, _jsx("iframe", { ref: iframeRef, className: "canvas-card__iframe", src: initialUrlRef.current, title: card.title || card.url, [CANVAS_RENDERER_ATTR]: "", sandbox: "allow-scripts allow-same-origin allow-forms allow-popups", "data-test": `canvas-card-iframe-${card.id}` }), _jsx(SketchFrameOverlay, { iframe: iframeRef.current, cardUrl: card.url, ready: loadState === "ready" })] }), presentation === "canvas" ? RESIZE_HANDLES.map((handle) => (_jsx("div", { className: `canvas-card__resize-handle canvas-card__resize-handle--${handle.direction}`, "data-test": `canvas-card-resize-${card.id}${handle.direction === "bottom-right" ? "" : `-${handle.direction}`}`, "data-resize-direction": handle.direction, style: {
455
+ }, onPointerDown: presentation === "canvas" ? handleCardPointerDown : undefined, children: [presentation === "canvas" ? _jsx(_Fragment, { children: _jsxs("div", { className: "canvas-card__header", style: { bottom: "100%", minHeight: `calc(var(--control-height-large) * ${toolbarScale})` }, children: [_jsxs("div", { className: "canvas-card__drag-surface", style: { minHeight: `calc(var(--control-height-large) * ${toolbarScale})` }, "data-test": `canvas-card-drag-${card.id}`, onPointerDown: handleToolbarPointerDown, children: [_jsx("div", { className: "canvas-card__identity", style: { zoom: toolbarScale }, children: _jsxs("span", { className: "canvas-card__dimensions", "data-test": `canvas-card-dimensions-${card.id}`, children: [!iterationId(card.content) ? (_jsx(Tooltip, { content: "Live view", children: _jsx("span", { className: "canvas-card__live-badge", "aria-label": "Live view", "data-test": `canvas-card-live-${card.id}`, children: _jsx(IconBoltFilled, { size: 12, "aria-hidden": "true" }) }) })) : (_jsx(Tooltip, { content: "HTML only", children: _jsx("span", { className: "canvas-card__iteration-badge", "aria-label": "HTML only", "data-test": `canvas-card-iteration-badge-${card.id}`, children: _jsx(IconArtboard, { size: 14, "aria-hidden": "true" }) }) })), _jsx("span", { className: "canvas-card__title", children: frameLabel })] }) }), iterationError ? _jsx("span", { className: "canvas-card__iteration-error", role: "status", children: iterationError }) : null] }), _jsx("div", { className: "canvas-card__floating-toolbar", "data-busy": creatingIteration ? "true" : undefined, onPointerDown: (event) => event.stopPropagation(), children: _jsxs("div", { className: "canvas-toolbar canvas-card__actions", style: { zoom: toolbarScale }, role: "toolbar", "aria-label": "Frame actions", "aria-busy": creatingIteration, "data-test": `canvas-card-actions-${card.id}`, children: [onShowFocus ? (_jsx(Tooltip, { content: "Open this frame in focus view", children: _jsxs(Button, { variant: "quiet", size: "default", "data-test": `canvas-card-focus-${card.id}`, onClick: () => onShowFocus(card.id), children: [_jsx(IconCornerLeftDown, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), "Focus"] }) })) : null, onDuplicate && !iterationId(card.content) ? (_jsx(Tooltip, { content: "Create a linked copy of this frame", children: _jsxs(Button, { variant: "quiet", size: "default", "data-test": `canvas-card-duplicate-${card.id}`, onClick: () => onDuplicate(card.id), children: [_jsx(IconCopy, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), "Duplicate"] }) })) : null, onCreateIteration ? (_jsx(Tooltip, { content: "Unlinked HTML version", stableTrigger: true, children: _jsxs(Button, { variant: "quiet", size: "default", "data-test": `canvas-card-create-iteration-${card.id}`, disabled: creatingIteration || loadState !== "ready", onClick: () => void handleIteration(), children: [_jsx(IconArtboard, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), creatingIteration ? "Creating…" : "Iteration"] }) })) : null, _jsx("div", { className: "canvas-toolbar__divider", "aria-hidden": "true" }), _jsx(Tooltip, { content: canRemove ? "Delete frame" : "Keep at least one frame on the canvas", children: _jsx(IconButton, { variant: "quiet", size: "default", label: "Delete frame", "data-test": `canvas-card-delete-${card.id}`, disabled: !canRemove, onClick: handleRemove, children: _jsx(IconTrash, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) }) })] }) })] }) }) : null, _jsxs("div", { className: "canvas-card__frame", children: [loadState === "loading" ? (_jsx("div", { className: "canvas-card__loading", "data-test": `canvas-card-loading-${card.id}`, children: "Loading preview..." })) : loadState === "error" ? (_jsxs("div", { className: "canvas-card__error", "data-test": `canvas-card-error-${card.id}`, children: [_jsx("p", { children: errorMessage || "Frame could not be loaded" }), _jsxs("div", { className: "canvas-card__error-actions", children: [_jsx(Button, { size: "compact", variant: "secondary", onClick: handleReload, children: "Retry" }), _jsx(Button, { size: "compact", variant: "danger", onClick: handleRemove, children: "Remove" })] })] })) : null, _jsx("iframe", { ref: iframeRef, className: "canvas-card__iframe", src: card.content.kind === "iteration" ? contentDocumentUrl(card.content) : initialUrlRef.current, title: frameLabel, [CANVAS_RENDERER_ATTR]: "", sandbox: "allow-scripts allow-same-origin allow-forms allow-popups", "data-test": `canvas-card-iframe-${card.id}` }, iterationId(card.content) ?? "live"), _jsx(AgentActivityOverlay, { cardId: card.id, content: card.content, iframe: loadState === "ready" ? iframeRef.current : null }), _jsx(SketchFrameOverlay, { cardId: card.id, iframe: iframeRef.current, cardUrl: contentSourceUrl(card.content), artifactId: iterationId(card.content), ready: loadState === "ready" })] }), presentation === "canvas" ? RESIZE_HANDLES.map((handle) => (_jsx("div", { className: `canvas-card__resize-handle canvas-card__resize-handle--${handle.direction}`, "data-test": `canvas-card-resize-${card.id}${handle.direction === "bottom-right" ? "" : `-${handle.direction}`}`, "data-resize-direction": handle.direction, style: {
359
456
  // Keep the edge's long axis aligned with the zoomed card. Only
360
457
  // the thickness needs inverse scaling to remain screen-sized.
361
458
  transform: resizeHandleTransform(handle.direction, resizeHandleScale),