nudge-ui 0.2.1 → 0.2.3

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 (258) hide show
  1. package/README.md +164 -46
  2. package/dist/client.mjs +252 -54
  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 +41 -32
  7. package/dist/hosts/astro/integration.d.ts +2 -2
  8. package/dist/hosts/astro/integration.js +9 -4
  9. package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
  10. package/dist/hosts/astro/markdownIdentity.js +81 -0
  11. package/dist/hosts/environment.d.ts +15 -0
  12. package/dist/hosts/environment.js +16 -0
  13. package/dist/hosts/next/manifest.js +1 -1
  14. package/dist/hosts/next/sidecar.js +12 -2
  15. package/dist/hosts/next/wrapper.d.ts +2 -0
  16. package/dist/hosts/next/wrapper.js +2 -1
  17. package/dist/hosts/projectBridge.d.ts +1 -0
  18. package/dist/hosts/projectBridge.js +1 -0
  19. package/dist/hosts/static/manifest.js +3 -1
  20. package/dist/hosts/static/server.js +4 -0
  21. package/dist/hosts/vite/vite.d.ts +4 -0
  22. package/dist/hosts/vite/vite.js +28 -5
  23. package/dist/inspector/agent/McpConnectionDialog.d.ts +3 -2
  24. package/dist/inspector/agent/McpConnectionDialog.js +23 -22
  25. package/dist/inspector/agent/activity.d.ts +18 -0
  26. package/dist/inspector/agent/activity.js +70 -0
  27. package/dist/inspector/agent/client.d.ts +8 -0
  28. package/dist/inspector/agent/client.js +65 -5
  29. package/dist/inspector/agent/connectionStatus.d.ts +3 -3
  30. package/dist/inspector/agent/connectionStatus.js +9 -2
  31. package/dist/inspector/agent/httpTransport.d.ts +6 -0
  32. package/dist/inspector/agent/httpTransport.js +50 -3
  33. package/dist/inspector/agent/protocol.d.ts +9 -3
  34. package/dist/inspector/agent/protocol.js +1 -1
  35. package/dist/inspector/agent/verification.d.ts +11 -1
  36. package/dist/inspector/agent/verification.js +53 -25
  37. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
  38. package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
  39. package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
  40. package/dist/inspector/canvas/CanvasCard.js +133 -38
  41. package/dist/inspector/canvas/CanvasElementOverlay.js +200 -22
  42. package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
  43. package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
  44. package/dist/inspector/canvas/CanvasToolbar.js +8 -4
  45. package/dist/inspector/canvas/CanvasWorkspace.js +243 -56
  46. package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
  47. package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
  48. package/dist/inspector/canvas/agentPresentation.js +64 -52
  49. package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
  50. package/dist/inspector/canvas/canvasGestures.js +1 -1
  51. package/dist/inspector/canvas/canvasStore.d.ts +46 -82
  52. package/dist/inspector/canvas/canvasStore.js +251 -284
  53. package/dist/inspector/canvas/frameContent.d.ts +21 -0
  54. package/dist/inspector/canvas/frameContent.js +44 -0
  55. package/dist/inspector/canvas/frameProtocol.d.ts +34 -4
  56. package/dist/inspector/canvas/frameProtocol.js +9 -2
  57. package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -1
  58. package/dist/inspector/canvas/keyboardShortcuts.js +5 -3
  59. package/dist/inspector/canvas/layout.d.ts +52 -0
  60. package/dist/inspector/canvas/layout.js +106 -0
  61. package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
  62. package/dist/inspector/canvas/linkEligibility.js +0 -29
  63. package/dist/inspector/canvas/projection.d.ts +15 -5
  64. package/dist/inspector/canvas/projection.js +69 -13
  65. package/dist/inspector/canvas/rendererBootstrap.js +45 -4
  66. package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
  67. package/dist/inspector/canvas/rendererElementSelector.js +102 -45
  68. package/dist/inspector/canvas/rendererStylesheet.js +2 -2
  69. package/dist/inspector/canvas/resizeHandleScale.js +1 -1
  70. package/dist/inspector/canvas/sessionStore.d.ts +11 -26
  71. package/dist/inspector/canvas/sessionStore.js +43 -183
  72. package/dist/inspector/canvas/staleChangeDetector.js +9 -3
  73. package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
  74. package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
  75. package/dist/inspector/canvas/viewStore.d.ts +38 -0
  76. package/dist/inspector/canvas/viewStore.js +104 -0
  77. package/dist/inspector/canvas/wheelPan.d.ts +8 -0
  78. package/dist/inspector/canvas/wheelPan.js +8 -0
  79. package/dist/inspector/changes/changesLog.d.ts +6 -5
  80. package/dist/inspector/changes/changesLog.js +40 -32
  81. package/dist/inspector/changes/draftChanges.d.ts +56 -0
  82. package/dist/inspector/changes/draftChanges.js +300 -0
  83. package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
  84. package/dist/inspector/changes/previewDiagnostics.js +11 -25
  85. package/dist/inspector/clientManifest.d.ts +3 -1
  86. package/dist/inspector/clientManifest.js +1 -0
  87. package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
  88. package/dist/inspector/comments/CommentsOverlay.js +250 -0
  89. package/dist/inspector/comments/element.d.ts +6 -0
  90. package/dist/inspector/comments/element.js +47 -0
  91. package/dist/inspector/comments/store.d.ts +29 -0
  92. package/dist/inspector/comments/store.js +121 -0
  93. package/dist/inspector/comments/verification.d.ts +5 -0
  94. package/dist/inspector/comments/verification.js +52 -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 +26 -35
  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 +173 -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.d.ts +10 -0
  111. package/dist/inspector/overlay/DomNavigator.js +46 -0
  112. package/dist/inspector/overlay/InspectorOverlay.js +6 -2
  113. package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
  114. package/dist/inspector/overlay/clickPolicy.js +34 -3
  115. package/dist/inspector/overlay/elementSelector.d.ts +1 -1
  116. package/dist/inspector/overlay/elementSelector.js +18 -7
  117. package/dist/inspector/overlay/interactionStyles.js +3 -1
  118. package/dist/inspector/overlay/spacingGestures.d.ts +15 -3
  119. package/dist/inspector/overlay/spacingGestures.js +88 -13
  120. package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
  121. package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
  122. package/dist/inspector/projection/renderedInstance.d.ts +2 -1
  123. package/dist/inspector/projection/renderedInstance.js +9 -5
  124. package/dist/inspector/projection/structuralProjection.d.ts +2 -1
  125. package/dist/inspector/projection/structuralProjection.js +19 -15
  126. package/dist/inspector/projection/textProjection.d.ts +2 -1
  127. package/dist/inspector/projection/textProjection.js +8 -4
  128. package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
  129. package/dist/inspector/prompt/clipboardHandoff.d.ts +10 -18
  130. package/dist/inspector/prompt/clipboardHandoff.js +92 -143
  131. package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
  132. package/dist/inspector/prompt/generatePrompt.js +17 -16
  133. package/dist/inspector/runtime/runtimeConfig.d.ts +10 -1
  134. package/dist/inspector/runtime/runtimeConfig.js +24 -0
  135. package/dist/inspector/selection/hierarchy.d.ts +6 -0
  136. package/dist/inspector/selection/hierarchy.js +8 -0
  137. package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
  138. package/dist/inspector/settings/SettingsDialog.js +22 -9
  139. package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
  140. package/dist/inspector/shell/AgentStatusButton.js +36 -0
  141. package/dist/inspector/shell/AppShell.js +4 -2
  142. package/dist/inspector/shell/ChangesLog.js +47 -27
  143. package/dist/inspector/shell/CopyPromptButton.js +146 -90
  144. package/dist/inspector/shell/EmptyState.js +9 -8
  145. package/dist/inspector/shell/InspectorShell.js +14 -25
  146. package/dist/inspector/shell/PeekOriginalButton.js +24 -23
  147. package/dist/inspector/shell/originalPreview.js +5 -5
  148. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  149. package/dist/inspector/shell/shortcuts.js +11 -0
  150. package/dist/inspector/sketch/SketchEditor.js +14 -14
  151. package/dist/inspector/sketch/SketchEntryButton.js +1 -1
  152. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
  153. package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
  154. package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
  155. package/dist/inspector/sketch/SketchOverlay.js +7 -7
  156. package/dist/inspector/sketch/SketchWorkspace.js +18 -12
  157. package/dist/inspector/sketch/interaction.d.ts +1 -0
  158. package/dist/inspector/sketch/interaction.js +8 -4
  159. package/dist/inspector/sketch/model.d.ts +5 -0
  160. package/dist/inspector/sketch/model.js +6 -2
  161. package/dist/inspector/sketch/raster.d.ts +1 -2
  162. package/dist/inspector/sketch/raster.js +68 -4
  163. package/dist/inspector/sketch/store.js +48 -23
  164. package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
  165. package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
  166. package/dist/inspector/styleEditors/BorderEditor.js +49 -78
  167. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
  168. package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
  169. package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
  170. package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
  171. package/dist/inspector/styleEditors/ColorPicker.js +10 -4
  172. package/dist/inspector/styleEditors/GapField.js +3 -5
  173. package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
  174. package/dist/inspector/styleEditors/GridChildSection.js +18 -9
  175. package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
  176. package/dist/inspector/styleEditors/GridPicker.js +8 -2
  177. package/dist/inspector/styleEditors/GridSection.js +1 -1
  178. package/dist/inspector/styleEditors/GridValueField.js +19 -14
  179. package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
  180. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
  181. package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
  182. package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
  183. package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
  184. package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
  185. package/dist/inspector/styleEditors/LayoutSection.js +32 -11
  186. package/dist/inspector/styleEditors/SpacingBox.js +16 -9
  187. package/dist/inspector/styleEditors/Typography.js +11 -3
  188. package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
  189. package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
  190. package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
  191. package/dist/inspector/styleEditors/gradientValue.js +114 -0
  192. package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
  193. package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
  194. package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
  195. package/dist/inspector/styleEditors/layoutValue.js +0 -42
  196. package/dist/inspector/styleEditors/nudgeValue.d.ts +4 -4
  197. package/dist/inspector/styleEditors/nudgeValue.js +41 -11
  198. package/dist/inspector/tokens/TokenField.d.ts +7 -0
  199. package/dist/inspector/tokens/TokenField.js +49 -22
  200. package/dist/inspector/tokens/TokenLabel.js +5 -14
  201. package/dist/inspector/tokens/TokensPanel.js +2 -1
  202. package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
  203. package/dist/inspector/tokens/dragPointerLock.js +70 -0
  204. package/dist/inspector/tokens/editActions.d.ts +1 -1
  205. package/dist/inspector/tokens/editActions.js +1 -1
  206. package/dist/inspector/ui/AtRuleContext.js +4 -6
  207. package/dist/inspector/ui/Button.d.ts +6 -2
  208. package/dist/inspector/ui/Button.js +4 -3
  209. package/dist/inspector/ui/ControlSurface.d.ts +2 -2
  210. package/dist/inspector/ui/ControlSurface.js +4 -3
  211. package/dist/inspector/ui/Disclosure.d.ts +22 -0
  212. package/dist/inspector/ui/Disclosure.js +13 -0
  213. package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
  214. package/dist/inspector/ui/InlineStyleWarning.js +13 -0
  215. package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
  216. package/dist/inspector/ui/InspectorPopover.js +4 -5
  217. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  218. package/dist/inspector/ui/PopoverListbox.js +8 -7
  219. package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
  220. package/dist/inspector/ui/SegmentedControl.js +2 -2
  221. package/dist/inspector/ui/Select.d.ts +6 -1
  222. package/dist/inspector/ui/Select.js +7 -11
  223. package/dist/inspector/ui/SideValuesField.js +1 -1
  224. package/dist/inspector/ui/Tooltip.d.ts +26 -0
  225. package/dist/inspector/ui/Tooltip.js +32 -0
  226. package/dist/inspector/ui/classNames.d.ts +2 -0
  227. package/dist/inspector/ui/classNames.js +4 -0
  228. package/dist/inspector/ui/customCursors.d.ts +23 -0
  229. package/dist/inspector/ui/customCursors.js +122 -0
  230. package/dist/inspector/ui/dragCursor.d.ts +6 -0
  231. package/dist/inspector/ui/dragCursor.js +13 -0
  232. package/dist/inspector/ui/portalContainer.d.ts +2 -0
  233. package/dist/inspector/ui/portalContainer.js +6 -0
  234. package/dist/inspector/ui/styles.js +34 -24
  235. package/dist/inspector/workspace/commands.d.ts +9 -0
  236. package/dist/inspector/workspace/commands.js +82 -0
  237. package/dist/inspector/workspace/controller.d.ts +4 -0
  238. package/dist/inspector/workspace/controller.js +66 -0
  239. package/dist/inspector/workspace/handoff.d.ts +42 -0
  240. package/dist/inspector/workspace/handoff.js +115 -0
  241. package/dist/inspector/workspace/timeline.d.ts +30 -0
  242. package/dist/inspector/workspace/timeline.js +108 -0
  243. package/dist/internal/agent-protocol.d.ts +19 -2
  244. package/dist/internal/agent-protocol.js +13 -0
  245. package/dist/nudge-ui.mjs +560 -299
  246. package/dist/project/artifacts.d.ts +13 -0
  247. package/dist/project/artifacts.js +253 -0
  248. package/dist/transport/artifacts.d.ts +8 -0
  249. package/dist/transport/artifacts.js +9 -0
  250. package/dist/transport/editor.d.ts +20 -1
  251. package/dist/transport/editor.js +44 -11
  252. package/dist/transport/routes.d.ts +6 -1
  253. package/dist/transport/routes.js +6 -1
  254. package/package.json +6 -2
  255. package/dist/inspector/changes/workspaceChanges.d.ts +0 -43
  256. package/dist/inspector/changes/workspaceChanges.js +0 -239
  257. package/dist/inspector/shell/DomNavigation.d.ts +0 -8
  258. package/dist/inspector/shell/DomNavigation.js +0 -30
@@ -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,16 @@ 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";
23
30
  import { getCanvasResizeEdges, resizeCanvasRect, } from "./canvasResize.js";
31
+ import { getDraftContentsForCard } from "../drafts/store.js";
32
+ import { removeFrame } from "../workspace/commands.js";
33
+ import { artifactDocumentUrl } from "../iterations/client.js";
24
34
  const RESIZE_HANDLES = [
25
35
  { direction: "top-left", cursor: "nwse-resize", transformOrigin: "left top", label: "Resize card from the top-left corner" },
26
36
  { direction: "top", cursor: "ns-resize", transformOrigin: "center top", label: "Resize card from the top edge" },
@@ -38,15 +48,23 @@ function resizeHandleTransform(direction, scale) {
38
48
  return `scale(${scale}, 1)`;
39
49
  return `scale(${scale})`;
40
50
  }
41
- export function CanvasCard({ card, presentation = "canvas", presentationCard = true, onShowFocus, documentOwner }) {
51
+ export function CanvasCard({ card, presentation = "canvas", presentationCard = true, linkedToActive = false, onShowFocus, onDuplicate, onCreateIteration, documentOwner }) {
42
52
  const iframeRef = useRef(null);
43
- const initialUrlRef = useRef(card.navigationUrl ?? card.url);
44
- const navigationUrlRef = useRef(card.navigationUrl);
53
+ const initialUrlRef = useRef(contentNavigationUrl(card.content) ?? contentSourceUrl(card.content));
54
+ const navigationUrlRef = useRef(contentNavigationUrl(card.content));
45
55
  const documentSessionRef = useRef(null);
46
56
  const frameRuntimeRef = useRef(null);
47
57
  const [loadState, setLoadState] = useState("loading");
48
58
  const [errorMessage, setErrorMessage] = useState(null);
59
+ const [creatingIteration, setCreatingIteration] = useState(false);
60
+ const [iterationError, setIterationError] = useState(null);
49
61
  const camera = useBoardCamera();
62
+ const runtimeConfig = useNudgeUiRuntimeConfig();
63
+ const canvasCards = useCanvasCards();
64
+ const canRemove = canvasCards.length > 1;
65
+ const frameLabel = runtimeConfig.demo === true && runtimeConfig.demoCardLabels?.length && card.title
66
+ ? card.title
67
+ : getCanvasCardLabel(card, canvasCards);
50
68
  const selectedCardId = useSelectedCardId();
51
69
  const focusedCardId = useFocusedCardId();
52
70
  const isSelected = selectedCardId === card.id;
@@ -94,22 +112,59 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
94
112
  disposeDocumentSession();
95
113
  unregisterCardFrame(card.id);
96
114
  };
97
- }, [bindDocumentSession, card.id, disposeDocumentSession]);
115
+ }, [bindDocumentSession, card.id, iterationId(card.content), disposeDocumentSession]);
116
+ useEffect(() => {
117
+ if (card.content.kind !== "iteration" || loadState !== "ready")
118
+ return;
119
+ const artifactId = card.content.artifactId;
120
+ let active = true;
121
+ let checking = false;
122
+ let reloadPending = false;
123
+ const timer = window.setInterval(() => {
124
+ const workspace = getDraftContentsForCard(card.id);
125
+ // Pending inspector edits still project over preview.html. Do not replace
126
+ // that base until they have been saved and handed off.
127
+ if (checking || isInlineTextEditingActive() || workspace?.changes.length || workspace?.structuralChanges.length)
128
+ return;
129
+ checking = true;
130
+ const frame = iframeRef.current;
131
+ if (!frame) {
132
+ checking = false;
133
+ return;
134
+ }
135
+ void refreshHtmlIteration(card.id, artifactId, frame).then((changed) => {
136
+ reloadPending ||= changed;
137
+ const latest = getDraftContentsForCard(card.id);
138
+ if (active && reloadPending && !isInlineTextEditingActive() && !latest?.changes.length && !latest?.structuralChanges.length) {
139
+ reloadPending = false;
140
+ loadFrame(`${artifactDocumentUrl(artifactId)}?revision=${Date.now()}`);
141
+ }
142
+ }).catch(() => undefined).finally(() => { checking = false; });
143
+ }, 1500);
144
+ return () => { active = false; window.clearInterval(timer); };
145
+ }, [iterationId(card.content), card.id, loadFrame, loadState]);
98
146
  function handleReload() {
99
147
  loadFrame();
100
148
  }
101
149
  function handleRemove() {
102
150
  if (getSelectedCardId() === card.id)
103
151
  setSelectedElement(null);
104
- removeCanvasCard(card.id);
152
+ removeFrame(card.id);
105
153
  }
106
- function updateFrameTitle(title) {
107
- if (!title)
108
- return;
109
- const runtime = getNudgeUiRuntimeConfig();
110
- if (runtime.demo === true && (runtime.demoCardLabels?.length ?? 0) > 0)
154
+ async function handleIteration(position) {
155
+ if (creatingIteration || !onCreateIteration || loadState !== "ready")
111
156
  return;
112
- updateCardTitle(card.id, title);
157
+ setCreatingIteration(true);
158
+ setIterationError(null);
159
+ try {
160
+ return await onCreateIteration(card.id, position);
161
+ }
162
+ catch (error) {
163
+ setIterationError(error instanceof Error ? error.message : "The iteration could not be created.");
164
+ }
165
+ finally {
166
+ setCreatingIteration(false);
167
+ }
113
168
  }
114
169
  /** The controller half of the handshake: announce workspace identity. */
115
170
  function sendParentReady() {
@@ -158,8 +213,7 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
158
213
  adoptFrameRuntime(data.runtime);
159
214
  setLoadState("ready");
160
215
  setErrorMessage(null);
161
- updateFrameTitle(data.title);
162
- if (data.url)
216
+ if (data.url && !iterationId(card.content))
163
217
  updateCardUrl(card.id, data.url);
164
218
  if (iframeRef.current) {
165
219
  registerCardFrame(card.id, iframeRef.current);
@@ -172,8 +226,7 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
172
226
  return;
173
227
  }
174
228
  if (data.type === "frame-metadata") {
175
- updateFrameTitle(data.title);
176
- if (data.url)
229
+ if (data.url && !iterationId(card.content))
177
230
  updateCardUrl(card.id, data.url);
178
231
  return;
179
232
  }
@@ -182,13 +235,13 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
182
235
  setErrorMessage(data.message || "Frame failed to load");
183
236
  }
184
237
  });
185
- }, [card.id]);
238
+ }, [card.id, iterationId(card.content)]);
186
239
  useLayoutEffect(() => {
187
- if (!card.navigationUrl || navigationUrlRef.current === card.navigationUrl)
240
+ if (!contentNavigationUrl(card.content) || navigationUrlRef.current === contentNavigationUrl(card.content))
188
241
  return;
189
- navigationUrlRef.current = card.navigationUrl;
190
- loadFrame(card.navigationUrl);
191
- }, [card.navigationUrl, loadFrame]);
242
+ navigationUrlRef.current = contentNavigationUrl(card.content);
243
+ loadFrame(contentNavigationUrl(card.content));
244
+ }, [contentNavigationUrl(card.content), loadFrame]);
192
245
  useEffect(() => {
193
246
  if (loadState !== "loading")
194
247
  return;
@@ -220,12 +273,13 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
220
273
  }
221
274
  iframe.addEventListener("load", onLoad);
222
275
  return () => iframe.removeEventListener("load", onLoad);
223
- }, [bindDocumentSession, card.id, disposeDocumentSession]);
276
+ }, [bindDocumentSession, card.id, iterationId(card.content), disposeDocumentSession]);
224
277
  const [isDragging, setIsDragging] = useState(false);
225
278
  const dragRef = useRef({ startX: 0, startY: 0, cardX: 0, cardY: 0 });
279
+ const dragCleanupRef = useRef(null);
226
280
  const resizeCleanupRef = useRef(null);
227
281
  useEffect(() => {
228
- return () => resizeCleanupRef.current?.();
282
+ return () => { resizeCleanupRef.current?.(); dragCleanupRef.current?.(); };
229
283
  }, []);
230
284
  const handleToolbarPointerDown = useCallback((e) => {
231
285
  // SAFETY: pointer events on the card target are HTMLElements in this DOM context.
@@ -237,6 +291,12 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
237
291
  selectCard(card.id);
238
292
  e.stopPropagation();
239
293
  e.preventDefault();
294
+ const iterationDrag = e.altKey && !!onCreateIteration && loadState === "ready" && !creatingIteration;
295
+ let moved = false;
296
+ let iterationId;
297
+ let creationStarted = false;
298
+ let cancelled = false;
299
+ let position = { x: card.x, y: card.y };
240
300
  dragRef.current = {
241
301
  startX: e.clientX,
242
302
  startY: e.clientY,
@@ -249,16 +309,56 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
249
309
  function onMove(ev) {
250
310
  const dx = (ev.clientX - dragRef.current.startX) / camera.zoom;
251
311
  const dy = (ev.clientY - dragRef.current.startY) / camera.zoom;
252
- setCardPosition(card.id, dragRef.current.cardX + dx, dragRef.current.cardY + dy);
312
+ moved ||= Math.hypot(ev.clientX - dragRef.current.startX, ev.clientY - dragRef.current.startY) >= 6;
313
+ position = { x: dragRef.current.cardX + dx, y: dragRef.current.cardY + dy };
314
+ if (iterationDrag) {
315
+ if (iterationId)
316
+ setCardPosition(iterationId, position.x, position.y);
317
+ else if (moved && !creationStarted) {
318
+ creationStarted = true;
319
+ void handleIteration(position).then((id) => {
320
+ if (!id)
321
+ return;
322
+ iterationId = id;
323
+ if (cancelled) {
324
+ removeFrame(id, { undoable: false });
325
+ selectCard(card.id);
326
+ }
327
+ else
328
+ setCardPosition(id, position.x, position.y);
329
+ });
330
+ }
331
+ }
332
+ else
333
+ setCardPosition(card.id, position.x, position.y);
253
334
  }
254
- function onUp() {
255
- setIsDragging(false);
335
+ function cleanup() {
256
336
  window.removeEventListener("pointermove", onMove);
257
337
  window.removeEventListener("pointerup", onUp);
338
+ window.removeEventListener("pointercancel", onCancel);
339
+ dragCleanupRef.current = null;
258
340
  }
341
+ function onCancel() {
342
+ cleanup();
343
+ setIsDragging(false);
344
+ cancelled = true;
345
+ if (iterationId) {
346
+ removeFrame(iterationId, { undoable: false });
347
+ selectCard(card.id);
348
+ }
349
+ }
350
+ function onUp() {
351
+ cleanup();
352
+ setIsDragging(false);
353
+ if (iterationId)
354
+ setCardPosition(iterationId, position.x, position.y);
355
+ }
356
+ dragCleanupRef.current?.();
357
+ dragCleanupRef.current = onCancel;
259
358
  window.addEventListener("pointermove", onMove);
260
359
  window.addEventListener("pointerup", onUp);
261
- }, [card.id, card.x, card.y, camera.zoom]);
360
+ window.addEventListener("pointercancel", onCancel);
361
+ }, [card.id, card.x, card.y, camera.zoom, onCreateIteration, loadState, creatingIteration]);
262
362
  const handleCardPointerDown = useCallback(() => {
263
363
  if (getSelectedCardId() !== card.id)
264
364
  setSelectedElement(null);
@@ -341,7 +441,8 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
341
441
  const next = resizeCanvasRect(card, direction, delta);
342
442
  resizeCard(card.id, next.width, next.height, { x: next.x, y: next.y });
343
443
  }
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: {
444
+ 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: {
445
+ "--canvas-toolbar-scale": toolbarScale,
345
446
  position: "absolute",
346
447
  visibility: presentation === "focus" && !presentationCard ? "hidden" : undefined,
347
448
  pointerEvents: presentation === "focus" && !presentationCard ? "none" : undefined,
@@ -349,13 +450,7 @@ export function CanvasCard({ card, presentation = "canvas", presentationCard = t
349
450
  top: isFocusPresentationCard ? 0 : card.y,
350
451
  width: isFocusPresentationCard ? "100%" : card.width,
351
452
  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: {
453
+ }, 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
454
  // Keep the edge's long axis aligned with the zoomed card. Only
360
455
  // the thickness needs inverse scaling to remain screen-sized.
361
456
  transform: resizeHandleTransform(handle.direction, resizeHandleScale),