nudge-ui 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/dist/cli/agentCommand.js +4 -28
  2. package/dist/client.mjs +870 -212
  3. package/dist/hosts/next/wrapper.js +1 -7
  4. package/dist/hosts/projectBridge.js +3 -17
  5. package/dist/inspector/agent/McpConnectionDialog.js +7 -3
  6. package/dist/inspector/agent/activity.d.ts +1 -1
  7. package/dist/inspector/agent/activity.js +3 -2
  8. package/dist/inspector/agent/client.d.ts +2 -2
  9. package/dist/inspector/agent/client.js +28 -29
  10. package/dist/inspector/agent/connectionStatus.d.ts +3 -1
  11. package/dist/inspector/agent/connectionStatus.js +25 -0
  12. package/dist/inspector/agent/httpTransport.js +31 -147
  13. package/dist/inspector/agent/protocol.d.ts +1 -1
  14. package/dist/inspector/agent/protocol.js +1 -1
  15. package/dist/inspector/agent/verification.d.ts +5 -7
  16. package/dist/inspector/agent/verification.js +67 -20
  17. package/dist/inspector/boot.d.ts +3 -0
  18. package/dist/inspector/boot.js +297 -0
  19. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +2 -1
  20. package/dist/inspector/canvas/AgentActivityOverlay.js +6 -3
  21. package/dist/inspector/canvas/CanvasCard.js +23 -12
  22. package/dist/inspector/canvas/CanvasElementOverlay.js +97 -128
  23. package/dist/inspector/canvas/CanvasToolbar.js +2 -1
  24. package/dist/inspector/canvas/CanvasWorkspace.js +119 -162
  25. package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +3 -2
  26. package/dist/inspector/canvas/LockedWorkspaceNotice.js +10 -6
  27. package/dist/inspector/canvas/PageCard.d.ts +10 -0
  28. package/dist/inspector/canvas/PageCard.js +51 -0
  29. package/dist/inspector/canvas/SketchToolOptions.d.ts +5 -0
  30. package/dist/inspector/canvas/SketchToolOptions.js +86 -0
  31. package/dist/inspector/canvas/StaleChangeIndicator.d.ts +1 -1
  32. package/dist/inspector/canvas/StaleChangeIndicator.js +1 -1
  33. package/dist/inspector/canvas/ToolOptionsDrawer.d.ts +21 -0
  34. package/dist/inspector/canvas/ToolOptionsDrawer.js +77 -0
  35. package/dist/inspector/canvas/activeCanvasDocument.d.ts +3 -2
  36. package/dist/inspector/canvas/assets/eraser.svg +28 -0
  37. package/dist/inspector/canvas/assets/pen.svg +25 -0
  38. package/dist/inspector/canvas/canvasFrame.d.ts +16 -0
  39. package/dist/inspector/canvas/canvasFrame.js +10 -0
  40. package/dist/inspector/canvas/canvasGestures.d.ts +0 -1
  41. package/dist/inspector/canvas/canvasGestures.js +0 -6
  42. package/dist/inspector/canvas/canvasStore.d.ts +6 -0
  43. package/dist/inspector/canvas/canvasStore.js +36 -1
  44. package/dist/inspector/canvas/frameConnection.d.ts +48 -0
  45. package/dist/inspector/canvas/frameConnection.js +73 -0
  46. package/dist/inspector/canvas/frameProtocol.d.ts +18 -1
  47. package/dist/inspector/canvas/frameProtocol.js +36 -0
  48. package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
  49. package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
  50. package/dist/inspector/canvas/pageSurface.d.ts +11 -0
  51. package/dist/inspector/canvas/pageSurface.js +34 -0
  52. package/dist/inspector/canvas/projection.d.ts +19 -9
  53. package/dist/inspector/canvas/projection.js +58 -31
  54. package/dist/inspector/canvas/rendererBootstrap.js +4 -1
  55. package/dist/inspector/canvas/rendererElementSelector.js +9 -1
  56. package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
  57. package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
  58. package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
  59. package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
  60. package/dist/inspector/canvas/rendererStylesheet.js +15 -0
  61. package/dist/inspector/canvas/sessionStore.d.ts +2 -2
  62. package/dist/inspector/canvas/sessionStore.js +12 -9
  63. package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
  64. package/dist/inspector/canvas/staleChangeDetector.js +10 -22
  65. package/dist/inspector/changes/codecs.d.ts +2 -0
  66. package/dist/inspector/changes/codecs.js +27 -3
  67. package/dist/inspector/changes/draftPreview.d.ts +12 -0
  68. package/dist/inspector/changes/draftPreview.js +133 -0
  69. package/dist/inspector/changes/editModel.d.ts +18 -0
  70. package/dist/inspector/changes/editModel.js +16 -3
  71. package/dist/inspector/changes/managedStyleProjection.js +2 -1
  72. package/dist/inspector/changes/previewDiagnostics.js +1 -1
  73. package/dist/inspector/changes/types.d.ts +18 -1
  74. package/dist/inspector/comments/CommentsOverlay.js +18 -16
  75. package/dist/inspector/comments/element.js +10 -3
  76. package/dist/inspector/comments/store.js +3 -2
  77. package/dist/inspector/comments/verification.js +1 -1
  78. package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
  79. package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
  80. package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
  81. package/dist/inspector/componentSemantics/changeAction.js +1 -1
  82. package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
  83. package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
  84. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
  85. package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
  86. package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
  87. package/dist/inspector/componentSemantics/textBinding.js +4 -45
  88. package/dist/inspector/componentSemantics/types.d.ts +11 -0
  89. package/dist/inspector/drafts/draftStore.d.ts +109 -0
  90. package/dist/inspector/drafts/draftStore.js +451 -0
  91. package/dist/inspector/drafts/model.d.ts +9 -1
  92. package/dist/inspector/drafts/model.js +5 -1
  93. package/dist/inspector/drafts/store.d.ts +53 -15
  94. package/dist/inspector/drafts/store.js +143 -169
  95. package/dist/inspector/index.d.ts +2 -4
  96. package/dist/inspector/index.js +2 -264
  97. package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
  98. package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
  99. package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
  100. package/dist/inspector/inspection/bridge.d.ts +1 -1
  101. package/dist/inspector/inspection/bridge.js +2 -1
  102. package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
  103. package/dist/inspector/inspection/browserCssInspection.js +28 -6
  104. package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
  105. package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
  106. package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
  107. package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
  108. package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
  109. package/dist/inspector/iterations/capture.d.ts +2 -1
  110. package/dist/inspector/iterations/capture.js +2 -1
  111. package/dist/inspector/iterations/client.d.ts +2 -1
  112. package/dist/inspector/iterations/coordinator.d.ts +4 -3
  113. package/dist/inspector/iterations/coordinator.js +5 -3
  114. package/dist/inspector/iterations/prompt.js +9 -8
  115. package/dist/inspector/overlay/interactionStyles.js +17 -0
  116. package/dist/inspector/pageInspector.d.ts +42 -0
  117. package/dist/inspector/pageInspector.js +342 -0
  118. package/dist/inspector/projection/draftProjection.d.ts +10 -2
  119. package/dist/inspector/projection/draftProjection.js +9 -1
  120. package/dist/inspector/projection/managedStylesheet.js +24 -4
  121. package/dist/inspector/projection/renderedInstance.d.ts +15 -2
  122. package/dist/inspector/projection/renderedInstance.js +60 -7
  123. package/dist/inspector/projection/structuralProjection.d.ts +0 -9
  124. package/dist/inspector/projection/structuralProjection.js +16 -46
  125. package/dist/inspector/projection/textProjection.d.ts +8 -2
  126. package/dist/inspector/projection/textProjection.js +77 -24
  127. package/dist/inspector/prompt/clipboardHandoff.d.ts +14 -0
  128. package/dist/inspector/prompt/clipboardHandoff.js +47 -16
  129. package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
  130. package/dist/inspector/prompt/generatePrompt.js +248 -148
  131. package/dist/inspector/prompt/promptDocument.d.ts +3 -0
  132. package/dist/inspector/prompt/promptDocument.js +8 -0
  133. package/dist/inspector/prompt/promptSettings.js +7 -5
  134. package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
  135. package/dist/inspector/runtime/inspectorStorage.js +42 -0
  136. package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
  137. package/dist/inspector/runtime/ownedStylesheets.js +72 -0
  138. package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
  139. package/dist/inspector/runtime/runtimeConfig.js +10 -1
  140. package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
  141. package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
  142. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
  143. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
  144. package/dist/inspector/selection/editScope.d.ts +1 -0
  145. package/dist/inspector/selection/editScope.js +13 -2
  146. package/dist/inspector/selection/sourceSite.d.ts +12 -0
  147. package/dist/inspector/selection/sourceSite.js +28 -0
  148. package/dist/inspector/shell/AgentStatusButton.js +2 -2
  149. package/dist/inspector/shell/AppShell.js +6 -2
  150. package/dist/inspector/shell/ChangesLog.js +27 -9
  151. package/dist/inspector/shell/CopyPromptButton.js +74 -26
  152. package/dist/inspector/shell/EmptyState.js +3 -3
  153. package/dist/inspector/shell/InspectorShell.d.ts +0 -3
  154. package/dist/inspector/shell/InspectorShell.js +21 -13
  155. package/dist/inspector/shell/PeekOriginalButton.js +3 -3
  156. package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
  157. package/dist/inspector/shell/interactionStatePreview.js +112 -0
  158. package/dist/inspector/shell/originalPreview.d.ts +3 -2
  159. package/dist/inspector/shell/originalPreview.js +10 -37
  160. package/dist/inspector/shell/stateValue.js +2 -3
  161. package/dist/inspector/sketch/SketchEditor.js +5 -4
  162. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
  163. package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
  164. package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
  165. package/dist/inspector/sketch/SketchOverlay.js +139 -263
  166. package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
  167. package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
  168. package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
  169. package/dist/inspector/sketch/SketchThumbnail.js +18 -0
  170. package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
  171. package/dist/inspector/sketch/SketchWorkspace.js +69 -34
  172. package/dist/inspector/sketch/capture.js +19 -3
  173. package/dist/inspector/sketch/copySketch.d.ts +10 -0
  174. package/dist/inspector/sketch/copySketch.js +52 -0
  175. package/dist/inspector/sketch/drawingTools.d.ts +17 -0
  176. package/dist/inspector/sketch/drawingTools.js +47 -0
  177. package/dist/inspector/sketch/frameHeading.d.ts +9 -0
  178. package/dist/inspector/sketch/frameHeading.js +30 -0
  179. package/dist/inspector/sketch/freehand.js +3 -3
  180. package/dist/inspector/sketch/geometry.d.ts +6 -0
  181. package/dist/inspector/sketch/geometry.js +19 -0
  182. package/dist/inspector/sketch/history.d.ts +30 -0
  183. package/dist/inspector/sketch/history.js +58 -0
  184. package/dist/inspector/sketch/interaction.d.ts +1 -3
  185. package/dist/inspector/sketch/interaction.js +6 -9
  186. package/dist/inspector/sketch/model.d.ts +35 -1
  187. package/dist/inspector/sketch/model.js +116 -61
  188. package/dist/inspector/sketch/persistence.d.ts +14 -0
  189. package/dist/inspector/sketch/persistence.js +63 -7
  190. package/dist/inspector/sketch/raster.d.ts +6 -2
  191. package/dist/inspector/sketch/raster.js +43 -17
  192. package/dist/inspector/sketch/store.d.ts +1 -1
  193. package/dist/inspector/sketch/store.js +1 -1
  194. package/dist/inspector/sketch/videoFrames.d.ts +6 -0
  195. package/dist/inspector/sketch/videoFrames.js +81 -0
  196. package/dist/inspector/styleEditors/BackgroundEditor.js +3 -2
  197. package/dist/inspector/styleEditors/BorderEditor.js +30 -130
  198. package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
  199. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
  200. package/dist/inspector/styleEditors/LayoutSection.js +2 -2
  201. package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
  202. package/dist/inspector/styleEditors/borderModel.js +114 -0
  203. package/dist/inspector/styleEditors/layoutValue.js +2 -3
  204. package/dist/inspector/tokens/TokenField.js +40 -7
  205. package/dist/inspector/tokens/TokensPanel.js +2 -1
  206. package/dist/inspector/tokens/editActions.d.ts +3 -3
  207. package/dist/inspector/tokens/editActions.js +15 -3
  208. package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
  209. package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
  210. package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
  211. package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
  212. package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
  213. package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
  214. package/dist/inspector/tokens/resolution.d.ts +6 -3
  215. package/dist/inspector/tokens/resolution.js +91 -112
  216. package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
  217. package/dist/inspector/tokens/tokenEdits.js +1 -1
  218. package/dist/inspector/ui/InlineStyleWarning.js +2 -2
  219. package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
  220. package/dist/inspector/ui/InspectorStyle.js +26 -0
  221. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  222. package/dist/inspector/ui/PopoverListbox.js +3 -3
  223. package/dist/inspector/ui/Toast.d.ts +14 -0
  224. package/dist/inspector/ui/Toast.js +30 -0
  225. package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
  226. package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
  227. package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
  228. package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
  229. package/dist/inspector/ui/customCursors.d.ts +5 -0
  230. package/dist/inspector/ui/customCursors.js +32 -6
  231. package/dist/inspector/ui/styles.js +12 -10
  232. package/dist/inspector/workspace/commands.js +1 -2
  233. package/dist/inspector/workspace/controller.js +1 -2
  234. package/dist/inspector/workspace/handoff.js +3 -2
  235. package/dist/inspector/workspace/sentItems.d.ts +16 -0
  236. package/dist/inspector/workspace/sentItems.js +33 -0
  237. package/dist/inspector/workspace/timeline.d.ts +24 -10
  238. package/dist/inspector/workspace/timeline.js +118 -96
  239. package/dist/internal/agent-protocol.d.ts +50 -13
  240. package/dist/internal/agent-protocol.js +145 -19
  241. package/dist/nudge-ui.mjs +81 -65
  242. package/dist/project/mcpPackage.d.ts +8 -0
  243. package/dist/project/mcpPackage.js +60 -0
  244. package/package.json +13 -3
  245. package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
  246. package/dist/inspector/canvas/measurementProjection.js +0 -16
  247. package/dist/inspector/changes/changesLog.d.ts +0 -47
  248. package/dist/inspector/changes/changesLog.js +0 -247
  249. package/dist/inspector/changes/draftChanges.d.ts +0 -56
  250. package/dist/inspector/changes/draftChanges.js +0 -300
  251. package/dist/inspector/conformance/projection.d.ts +0 -2
  252. package/dist/inspector/conformance/projection.js +0 -3
  253. package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
  254. package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
  255. package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
  256. package/dist/inspector/overlay/InspectorOverlay.js +0 -332
  257. package/dist/inspector/overlay/elementSelector.d.ts +0 -1
  258. package/dist/inspector/overlay/elementSelector.js +0 -111
  259. package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
  260. package/dist/inspector/sketch/SketchChanges.js +0 -72
  261. package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
  262. package/dist/inspector/sketch/SketchEntryButton.js +0 -11
  263. package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
  264. package/dist/inspector/sketch/SketchLayersPanel.js +0 -71
@@ -2,14 +2,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { InspectorShell } from "./InspectorShell.js";
3
3
  import { CanvasWorkspace } from "../canvas/CanvasWorkspace.js";
4
4
  import { CanvasElementOverlay } from "../canvas/CanvasElementOverlay.js";
5
+ import { isPageSurface } from "../canvas/pageSurface.js";
5
6
  import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
6
7
  import { readNudgeUiEditorTarget } from "../../transport/editor.js";
7
8
  import { TooltipGroup } from "../ui/Tooltip.js";
9
+ import { ToastRegion } from "../ui/Toast.js";
8
10
  import { CommentsOverlay } from "../comments/CommentsOverlay.js";
9
11
  export function AppShell() {
10
12
  const canvasEnabled = useNudgeUiRuntimeConfig().capabilities.canvas;
11
13
  const primaryUrl = typeof window === "undefined"
12
14
  ? null
13
- : readNudgeUiEditorTarget(window.location.href);
14
- return (_jsxs(TooltipGroup, { children: [canvasEnabled ? _jsx(CanvasWorkspace, { primaryUrl: primaryUrl }) : null, canvasEnabled ? _jsx(CanvasElementOverlay, {}) : null, canvasEnabled ? _jsx(CommentsOverlay, {}) : null, _jsx(InspectorShell, {})] }));
15
+ : isPageSurface()
16
+ ? window.location.href
17
+ : readNudgeUiEditorTarget(window.location.href);
18
+ return (_jsxs(TooltipGroup, { children: [canvasEnabled ? _jsx(CanvasWorkspace, { primaryUrl: primaryUrl }) : null, canvasEnabled ? _jsx(CanvasElementOverlay, {}) : null, canvasEnabled ? _jsx(CommentsOverlay, {}) : null, _jsx(InspectorShell, {}), _jsx(ToastRegion, {})] }));
15
19
  }
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useMemo, useSyncExternalStore } from "react";
2
+ import { useMemo, useState, useSyncExternalStore } from "react";
3
3
  import { IconArrowBackUp } from "@tabler/icons-react";
4
- import { isComponentChange, isElementChange, isTextContentChange, isTokenChange, useChanges, revertChange, } from "../changes/changesLog.js";
4
+ import { isComponentChange, isElementChange, isTextContentChange, isTokenChange, } from "../changes/types.js";
5
+ import { useChanges, revertChange } from "../drafts/store.js";
5
6
  import { StaleChangeIndicator } from "../canvas/StaleChangeIndicator.js";
6
7
  import { Button } from "../ui/Button.js";
7
8
  import { Disclosure } from "../ui/Disclosure.js";
@@ -11,8 +12,8 @@ import { presentChange } from "../changes/presentation.js";
11
12
  import { getStructuralChanges, getStructuralChangeDiagnostics, getStructuralDiagnosticRevision, revertStructuralChange, subscribeStructuralChanges, subscribeStructuralDiagnostics, } from "../projection/structuralProjection.js";
12
13
  import { getRenderedInstanceChangeDiagnostics, getRenderedInstanceDiagnosticRevision, subscribeRenderedInstanceDiagnostics, } from "../projection/renderedInstance.js";
13
14
  import { getTextContentChangeDiagnostics, getTextProjectionDiagnosticRevision, subscribeTextProjectionDiagnostics, } from "../projection/textProjection.js";
14
- import { SketchChanges } from "../sketch/SketchChanges.js";
15
- import { useSketchStore } from "../sketch/store.js";
15
+ import { removeSketch, useSketchStore } from "../sketch/store.js";
16
+ import { StatusCallout } from "../ui/StatusCallout.js";
16
17
  import { useFrameComments } from "../comments/store.js";
17
18
  import { countPromptChanges } from "../prompt/generatePrompt.js";
18
19
  function groupChanges(changes) {
@@ -75,8 +76,25 @@ function changeActionLabel(change, presentation) {
75
76
  return "Changed text";
76
77
  return `Changed ${presentation.propertyLabel}`;
77
78
  }
78
- function RevertButton({ dataTest, dataProperty, onClick, }) {
79
- return (_jsx(Tooltip, { content: "Revert", stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", size: "compact", className: "changes__revert", "data-test": dataTest, "data-property": dataProperty, label: "Revert change", onClick: onClick, children: _jsx(IconArrowBackUp, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) }) }));
79
+ function RevertButton({ dataTest, dataProperty, onClick, disabled, label = "Revert change", tooltip = "Revert", }) {
80
+ return (_jsx(Tooltip, { content: tooltip, stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", size: "compact", className: "changes__revert", "data-test": dataTest, "data-property": dataProperty, label: label, disabled: disabled, onClick: onClick, children: _jsx(IconArrowBackUp, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) }) }));
81
+ }
82
+ function SketchChangeRow({ item }) {
83
+ const [error, setError] = useState(null);
84
+ const note = item.document.description.trim()
85
+ || item.document.annotations?.map((annotation) => annotation.description).join("; ");
86
+ const busy = item.status === "dispatching" || item.status === "handing-off";
87
+ async function remove() {
88
+ setError(null);
89
+ try {
90
+ if (!await removeSketch(item.document.id))
91
+ setError("The sketch could not be removed yet.");
92
+ }
93
+ catch (cause) {
94
+ setError(cause instanceof Error ? cause.message : "The sketch could not be removed.");
95
+ }
96
+ }
97
+ return (_jsxs("div", { className: "changes__group", children: [_jsxs("div", { className: "changes__row changes__row--sketch", "data-test": "sketch-row", "data-sketch-id": item.document.id, children: [_jsxs("div", { className: "changes__summary", children: [_jsx("span", { className: "changes__target", children: "Sketch" }), note ? _jsx("span", { className: "changes__action", title: note, children: note }) : null] }), _jsx(RevertButton, { dataTest: "sketch-revert", label: "Remove sketch", tooltip: "Remove sketch", disabled: busy, onClick: () => void remove() })] }), error ? _jsx(StatusCallout, { tone: "warning", role: "status", children: error }) : null] }));
80
98
  }
81
99
  export function ChangesLog({ onClearSession }) {
82
100
  const changes = useChanges();
@@ -103,8 +121,8 @@ export function ChangesLog({ onClearSession }) {
103
121
  const textDiagnostics = change.kind === "text-content"
104
122
  ? getTextContentChangeDiagnostics(change.id)
105
123
  : [];
106
- return (_jsxs("div", { className: "changes__row", "data-test": "change-row", "data-property": presentation.property, children: [_jsx("span", { className: "changes__target", "data-test": "change-target", children: changeTargetLabel(change, presentation) }), _jsx("span", { className: "changes__action", "data-test": "change-action", children: changeActionLabel(change, presentation) }), _jsxs("span", { className: "changes__metadata", "aria-hidden": "true", children: [_jsx("span", { className: "changes__prop", children: presentation.propertyLabel }), _jsxs("span", { className: "changes__value", children: [_jsx("span", { className: "changes__before", children: presentation.before }), _jsx("span", { className: "changes__arrow", children: "\u2192" }), _jsx("span", { className: "changes__after", children: presentation.after })] }), presentation.scope ? (_jsx("span", { className: "changes__scope", "data-test": "change-scope", children: presentation.scope === "source-site" ? "All outputs at source site" : "This rendered item only" })) : null, presentation.evidence ? (_jsx("span", { className: "changes__evidence", "data-test": "change-evidence", children: presentation.evidence })) : null, _jsx(StaleChangeIndicator, { change: change }), instanceDiagnostics.map((diagnostic) => (_jsxs("span", { className: "changes__diagnostic", "data-test": "instance-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, children: [diagnostic.document, ": ", diagnostic.status] }, `${diagnostic.document}:${diagnostic.status}`))), textDiagnostics.map((diagnostic) => (_jsxs("span", { className: "changes__diagnostic", "data-test": "text-projection-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, children: [diagnostic.document, ": ", diagnostic.status] }, `${diagnostic.document}:${diagnostic.status}`)))] }), _jsx(RevertButton, { dataTest: "change-revert", dataProperty: presentation.property, onClick: () => revertChange(change) })] }, `${group.key}\u0000${presentation.property}\u0000${i}`));
107
- }) }, group.key))), structuralChanges.map((change) => (_jsx("div", { className: "changes__group", "data-test": "dom-change", "data-cid": change.target.sourceSite.cid, children: _jsxs("div", { className: "changes__row", "data-test": "dom-change-row", "data-action": change.kind, children: [_jsx("span", { className: "changes__target", "data-test": "structural-target", children: change.target.sourceSite.cid }), _jsx("span", { className: "changes__action", "data-test": "structural-action", children: change.kind === "move" ? "Moved" : "Deleted" }), _jsxs("span", { className: "changes__metadata", "aria-hidden": "true", children: [_jsxs("span", { className: "changes__source-site", "data-test": "structural-source-site", children: ["Source site: ", change.target.sourceSite.cid] }), _jsx("span", { className: "changes__prop", children: change.kind === "move" ? "Move in DOM" : "Delete from DOM" }), _jsxs("span", { className: "changes__value", children: [_jsx("span", { className: "changes__before", children: change.kind === "move"
124
+ return (_jsxs("div", { className: "changes__row", "data-test": "change-row", "data-property": presentation.property, children: [_jsxs("div", { className: "changes__summary", children: [_jsx("span", { className: "changes__target", "data-test": "change-target", children: changeTargetLabel(change, presentation) }), _jsx("span", { className: "changes__action", "data-test": "change-action", children: changeActionLabel(change, presentation) })] }), _jsxs("span", { className: "changes__metadata", "aria-hidden": "true", children: [_jsx("span", { className: "changes__prop", children: presentation.propertyLabel }), _jsxs("span", { className: "changes__value", children: [_jsx("span", { className: "changes__before", children: presentation.before }), _jsx("span", { className: "changes__arrow", children: "\u2192" }), _jsx("span", { className: "changes__after", children: presentation.after })] }), presentation.scope ? (_jsx("span", { className: "changes__scope", "data-test": "change-scope", children: presentation.scope === "source-site" ? "All outputs at source site" : "This rendered item only" })) : null, presentation.evidence ? (_jsx("span", { className: "changes__evidence", "data-test": "change-evidence", children: presentation.evidence })) : null, _jsx(StaleChangeIndicator, { change: change }), instanceDiagnostics.map((diagnostic) => (_jsxs("span", { className: "changes__diagnostic", "data-test": "instance-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, children: [diagnostic.document, ": ", diagnostic.status] }, `${diagnostic.document}:${diagnostic.status}`))), textDiagnostics.map((diagnostic) => (_jsxs("span", { className: "changes__diagnostic", "data-test": "text-projection-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, children: [diagnostic.document, ": ", diagnostic.status] }, `${diagnostic.document}:${diagnostic.status}`)))] }), _jsx(RevertButton, { dataTest: "change-revert", dataProperty: presentation.property, onClick: () => revertChange(change) })] }, `${group.key}\u0000${presentation.property}\u0000${i}`));
125
+ }) }, group.key))), structuralChanges.map((change) => (_jsx("div", { className: "changes__group", "data-test": "dom-change", "data-cid": change.target.sourceSite.cid, children: _jsxs("div", { className: "changes__row", "data-test": "dom-change-row", "data-action": change.kind, children: [_jsxs("div", { className: "changes__summary", children: [_jsx("span", { className: "changes__target", "data-test": "structural-target", children: change.target.sourceSite.cid }), _jsx("span", { className: "changes__action", "data-test": "structural-action", children: change.kind === "move" ? "Moved" : "Deleted" })] }), _jsxs("span", { className: "changes__metadata", "aria-hidden": "true", children: [_jsxs("span", { className: "changes__source-site", "data-test": "structural-source-site", children: ["Source site: ", change.target.sourceSite.cid] }), _jsx("span", { className: "changes__prop", children: change.kind === "move" ? "Move in DOM" : "Delete from DOM" }), _jsxs("span", { className: "changes__value", children: [_jsx("span", { className: "changes__before", children: change.kind === "move"
108
126
  ? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
109
127
  ? `${change.presentation.sourceParentTag} position ${change.presentation.fromIndex + 1}`
110
128
  : `${structuralParentLabel(change, "source")} position ${change.presentation.fromIndex + 1}`
@@ -112,5 +130,5 @@ export function ChangesLog({ onClearSession }) {
112
130
  ? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
113
131
  ? `${change.presentation.destinationParentTag} position ${change.presentation.toIndex + 1}`
114
132
  : `${structuralParentLabel(change, "destination")} position ${change.presentation.toIndex + 1}`
115
- : "Removed" })] }), _jsx("span", { className: "changes__scope", "data-test": "structural-scope", children: "This rendered item only" }), getStructuralChangeDiagnostics(change.id).map((diagnostic) => (_jsx("span", { className: "changes__diagnostic", "data-test": "structural-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, "data-reason": diagnostic.reason, children: structuralDiagnosticText(diagnostic) }, `${diagnostic.document}:${diagnostic.status}:${diagnostic.reason ?? ""}`)))] }), _jsx(RevertButton, { dataTest: "dom-change-revert", onClick: () => revertStructuralChange(change.id) })] }) }, change.id))), _jsx(SketchChanges, {})] })) }) : null, onClearSession ? (_jsx("div", { className: "changes__session-action", "data-test": "session-actions", children: _jsx(Button, { variant: "secondary", type: "button", "data-test": "clear-session", onClick: onClearSession, children: "Clear Changes" }) })) : null] }));
133
+ : "Removed" })] }), _jsx("span", { className: "changes__scope", "data-test": "structural-scope", children: "This rendered item only" }), getStructuralChangeDiagnostics(change.id).map((diagnostic) => (_jsx("span", { className: "changes__diagnostic", "data-test": "structural-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, "data-reason": diagnostic.reason, children: structuralDiagnosticText(diagnostic) }, `${diagnostic.document}:${diagnostic.status}:${diagnostic.reason ?? ""}`)))] }), _jsx(RevertButton, { dataTest: "dom-change-revert", onClick: () => revertStructuralChange(change.id) })] }) }, change.id))), sketches.map((item) => _jsx(SketchChangeRow, { item: item }, `${item.document.id}:${item.document.revision}`)), sketchStore.error ? _jsx(StatusCallout, { tone: "warning", "data-test": "sketch-storage-error", children: sketchStore.error }) : null] })) }) : null, onClearSession ? (_jsx("div", { className: "changes__session-action", "data-test": "session-actions", children: _jsx(Button, { variant: "secondary", type: "button", "data-test": "clear-session", onClick: onClearSession, children: "Clear Changes" }) })) : null] }));
116
134
  }
@@ -1,12 +1,12 @@
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
2
  import { Menu } from "@base-ui/react/menu";
3
3
  import { iterationId } from "../canvas/frameContent.js";
4
4
  import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
- import { IconArtboard, IconBoltFilled, IconCheck, IconChevronDown, IconCopy, IconPlugConnected, IconSend, } from "@tabler/icons-react";
6
- import { useChanges } from "../changes/changesLog.js";
5
+ import { IconArtboard, IconBoltFilled, IconCheck, IconChevronDown, IconCopy, IconPencil, IconPlugConnected, IconSend, IconX, } from "@tabler/icons-react";
6
+ import { useChanges } from "../drafts/store.js";
7
7
  import { countPromptChanges } from "../prompt/generatePrompt.js";
8
8
  import { loadCustomInstructions, saveCustomInstructions } from "../prompt/promptSettings.js";
9
- import { getAgentConnectionStatus } from "../agent/connectionStatus.js";
9
+ import { useAgentConnectionStatus } from "../agent/connectionStatus.js";
10
10
  import { SettingsDialog } from "../settings/SettingsDialog.js";
11
11
  import { Button } from "../ui/Button.js";
12
12
  import { portalContainer } from "../ui/portalContainer.js";
@@ -15,10 +15,15 @@ import { getStructuralChanges, subscribeStructuralChanges } from "../projection/
15
15
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
16
16
  import { getAgentClient, useAgentClient } from "../agent/client.js";
17
17
  import { createAgentPresentationAdapter } from "../canvas/agentPresentation.js";
18
- import { verifyAndReconcileAgentDispatch } from "../agent/verification.js";
18
+ import { captureHandoffOwner, getAgentDispatch, verifyAndReconcileAgentDispatch, } from "../agent/verification.js";
19
+ import { clearSentItems, getSentItems, sentItemCount } from "../workspace/sentItems.js";
19
20
  import { getClipboardHandoffRevision, getLastClipboardReconciledCount, subscribeClipboardHandoff, } from "../prompt/clipboardHandoff.js";
20
- import { useSketches, settleSketchDispatch } from "../sketch/store.js";
21
- import { SketchLayersPanel } from "../sketch/SketchLayersPanel.js";
21
+ import { removeSketch, useSketches, settleSketchDispatch } from "../sketch/store.js";
22
+ import { SketchThumbnail } from "../sketch/SketchThumbnail.js";
23
+ import { copySketchImageOnRequest } from "../sketch/copySketch.js";
24
+ import { closeSketchNote } from "../sketch/sketchNote.js";
25
+ import { IconButton } from "../ui/IconButton.js";
26
+ import { showToast } from "../ui/Toast.js";
22
27
  import { isSendPromptShortcut, SEND_PROMPT_HOTKEY_EVENT } from "./shortcuts.js";
23
28
  import { getFocusedCardId, getSelectedCardId, useCanvasCards, useFocusedCardId, useSelectedCardId } from "../canvas/canvasStore.js";
24
29
  import { getEditableCardId, sketchBelongsToCard, useDrafts } from "../drafts/store.js";
@@ -47,7 +52,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
47
52
  const [preparing, setPreparing] = useState(false);
48
53
  const handoffBusy = useRef(false);
49
54
  const [saveMessage, setSaveMessage] = useState(null);
50
- const [agentCompletionStatus, setAgentCompletionStatus] = useState(null);
55
+ const [agentOutcome, setAgentOutcome] = useState(null);
51
56
  const [sketchFallback, setSketchFallback] = useState(null);
52
57
  const [localSettingsOpen, setLocalSettingsOpen] = useState(false);
53
58
  const [localSettingsSection, setLocalSettingsSection] = useState("instructions");
@@ -85,6 +90,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
85
90
  if (agent.state !== "completed" || revision === undefined)
86
91
  return;
87
92
  let active = true;
93
+ const dispatch = getAgentDispatch(revision);
88
94
  void verifyAndReconcileAgentDispatch(revision)
89
95
  .then(async (removed) => {
90
96
  for (const [, iframe] of getRegisteredFrames()) {
@@ -99,7 +105,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
99
105
  const latest = agentRef.current;
100
106
  if (latest.request?.changeRevision !== revision || latest.state !== "completed")
101
107
  return;
102
- setAgentCompletionStatus(removed > 0 ? "verified" : null);
108
+ setAgentOutcome({ removed, dispatch });
103
109
  })
104
110
  .catch(() => {
105
111
  if (!active)
@@ -107,7 +113,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
107
113
  const latest = agentRef.current;
108
114
  if (latest.request?.changeRevision !== revision || latest.state !== "completed")
109
115
  return;
110
- setAgentCompletionStatus(null);
116
+ setAgentOutcome({ removed: 0, dispatch });
111
117
  });
112
118
  return () => {
113
119
  active = false;
@@ -120,16 +126,20 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
120
126
  void settleSketchDispatch(request.changeRevision, request.status, request.requestId, request.clientDispatchId).catch(() => undefined);
121
127
  }, [agent.request?.changeRevision, agent.request?.requestId, agent.request?.clientDispatchId, agent.request?.sketches, agent.request?.status]);
122
128
  const connecting = agent.state === "pairing";
129
+ const delivered = agent.state === "delivered" || agent.request?.status === "delivered";
123
130
  const working = agent.state === "working" || agent.request?.status === "working";
124
131
  const canConnect = agent.companionReachable
125
132
  && agent.listenerActive
126
133
  && !agent.paired
127
134
  && !connecting
128
- && !working;
129
- const canSend = agent.paired && agent.listenerActive && !working;
135
+ && !working
136
+ && !delivered;
137
+ const canSend = agent.paired && agent.listenerActive && !working && !delivered;
130
138
  const changeCount = countPromptChanges(changes, structuralChanges, canSend ? pendingSketches.length : 0) + comments.length;
131
- const disabled = preparing || connecting || working || (!canConnect && !hasChanges);
132
- const agentStatus = getAgentConnectionStatus(agent);
139
+ // The badge counts every pending sketch, including ones copied separately from the menu.
140
+ const badgeCount = countPromptChanges(changes, structuralChanges, pendingSketches.length) + comments.length;
141
+ const disabled = preparing || connecting || working || delivered || (!canConnect && !hasChanges);
142
+ const agentStatus = useAgentConnectionStatus(agent);
133
143
  const statusAction = agentStatus.action;
134
144
  const label = preparing
135
145
  ? "Saving iteration…"
@@ -137,18 +147,20 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
137
147
  ? "Copied!"
138
148
  : connecting
139
149
  ? "Connecting…"
140
- : working
141
- ? "Agent working…"
142
- : canConnect && !activeArtifactId
143
- ? "Connect agent"
144
- : canSend && !activeArtifactId
145
- ? "Send prompt"
146
- : "Copy prompt";
150
+ : delivered
151
+ ? "Awaiting agent…"
152
+ : working
153
+ ? "Agent working…"
154
+ : canConnect && !activeArtifactId
155
+ ? "Connect agent"
156
+ : canSend && !activeArtifactId
157
+ ? "Send prompt"
158
+ : "Copy prompt";
147
159
  const icon = copied
148
160
  ? _jsx(IconCheck, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
149
161
  : !activeArtifactId && (canConnect || connecting)
150
162
  ? _jsx(IconPlugConnected, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
151
- : !activeArtifactId && (canSend || working)
163
+ : !activeArtifactId && (canSend || working || delivered)
152
164
  ? _jsx(IconSend, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
153
165
  : _jsx(IconCopy, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" });
154
166
  async function onClick() {
@@ -227,7 +239,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
227
239
  setSaveMessage(error instanceof Error ? error.message : "The HTML iteration could not be saved.");
228
240
  return;
229
241
  }
230
- setAgentCompletionStatus(null);
242
+ setAgentOutcome(null);
231
243
  setSketchFallback(null);
232
244
  const outcome = await deliverHandoff(prepared, runtimeConfig.projectId, canSend ? agentClient : null);
233
245
  if (outcome.kind === "sketch-failed") {
@@ -238,6 +250,14 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
238
250
  return;
239
251
  setCopied(true);
240
252
  }
253
+ const handoffOwner = captureHandoffOwner(activeDraftCardId);
254
+ const sentItems = getSentItems(handoffOwner.draftId, handoffOwner.target, agentOutcome?.dispatch ?? null);
255
+ const remainingCount = sentItemCount(sentItems);
256
+ const showImplemented = (agentOutcome?.removed ?? 0) > 0 || reconciledCount > 0
257
+ || (remainingCount > 0 && agentOutcome !== null);
258
+ function clearRemaining() {
259
+ clearSentItems(sentItems);
260
+ }
241
261
  useEffect(() => {
242
262
  function onKeydown(event) {
243
263
  if (!isSendPromptShortcut(event) || !canSend || !hasChanges || disabled)
@@ -284,8 +304,36 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
284
304
  }
285
305
  openMcpConnection();
286
306
  }
287
- const promptButton = (_jsxs(Button, { variant: "primary", className: "copy-prompt__main", "data-test": "copy-prompt", type: "button", disabled: disabled, "data-copied": copied ? "true" : "false", "data-agent-state": agent.state, "aria-busy": preparing || working || connecting ? "true" : undefined, onClick: activeArtifactId ? undefined : onClick, children: [icon, label, changeCount > 0 ? (_jsx("span", { className: "copy-prompt__change-count", "data-test": "copy-prompt-change-count", children: changeCount })) : null, activeArtifactId ? _jsx(IconChevronDown, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) : null] }));
288
- return (_jsxs("div", { className: "copy-prompt__stack", "data-test": "copy-prompt-control", children: [activeArtifactId ? (_jsxs(Menu.Root, { modal: false, children: [_jsx(Menu.Trigger, { render: promptButton }), _jsx(Menu.Portal, { container: portalContainer(), children: _jsx(Menu.Positioner, { className: "inspector-popover__positioner", side: "bottom", align: "start", sideOffset: 6, children: _jsxs(Menu.Popup, { className: "inspector-popover__popup copy-prompt__menu", "data-test": "copy-prompt-menu", children: [_jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-prompt", label: "Copy iteration prompt", onClick: () => void copyIterationPrompt("iteration"), children: [_jsx(IconArtboard, { className: "copy-prompt__menu-icon", size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy iteration prompt" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Continue editing this HTML iteration." })] })] }), _jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-implementation", label: "Copy prompt for live app", onClick: () => void copyIterationPrompt("application"), children: [_jsx(IconBoltFilled, { className: "copy-prompt__menu-icon copy-prompt__menu-icon--live", size: "var(--icon-size-small)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy prompt for live app" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Implement design in real app" })] })] })] }) }) })] }, activeDraftCardId)) : promptButton, _jsx(SketchLayersPanel, {}), statusAction || agentStatus.kind === "connected-not-listening" ? (_jsx(StatusCallout, { className: "copy-prompt__agent-status", tone: agentStatus.tone, "data-test": "agent-connection-status", children: _jsxs("div", { className: "copy-prompt__agent-status-content", children: [_jsx("span", { role: "status", children: agentStatus.label }), statusAction ? (_jsx(Button, { size: "compact", variant: "quiet", "data-test": "agent-status-action", "data-action": statusAction.kind, type: "button", onClick: () => void handleAgentStatusAction(), children: statusAction.label })) : null] }) })) : null, reconciledCount > 0 ? (_jsxs("p", { className: "copy-prompt__hint copy-prompt__hint--centered", "data-test": "clipboard-reconciled-hint", role: "status", children: [reconciledCount, " Implemented changes"] })) : null, saveMessage ? (_jsx("p", { className: "copy-prompt__hint copy-prompt__hint--centered", "data-test": "iteration-save-hint", role: "status", children: saveMessage })) : null, agentCompletionStatus === "verified" ? (_jsx("p", { className: "copy-prompt__hint copy-prompt__hint--centered copy-prompt__hint--implemented", "data-test": "agent-verified-hint", role: "status", children: "Changes implemented" })) : null, sketchFallback ? (_jsxs(StatusCallout, { className: "copy-prompt__sketch-fallback", tone: "warning", "data-test": "sketch-dispatch-error", children: [_jsx("span", { children: sketchFallback.error }), _jsx(Button, { size: "compact", variant: "secondary", type: "button", "data-test": "sketch-copy-fallback", onClick: () => {
289
- void copyHandoff(sketchFallback.handoff).then(() => setSketchFallback(null)).catch(() => undefined);
307
+ async function deleteSketch(sketchId) {
308
+ try {
309
+ if (!await removeSketch(sketchId)) {
310
+ showToast({ tone: "error", title: "Sketch not deleted", description: "This sketch is being sent and cannot be deleted yet." });
311
+ return;
312
+ }
313
+ closeSketchNote();
314
+ }
315
+ catch (error) {
316
+ showToast({ tone: "error", title: "Sketch not deleted", description: error instanceof Error ? error.message : "The sketch could not be deleted." });
317
+ }
318
+ }
319
+ const hasMenu = Boolean(activeArtifactId) || visibleSketches.length > 0;
320
+ const styleEditLabel = canConnect ? "Connect agent" : canSend ? "Send style edit prompt" : "Copy style edit prompt";
321
+ const styleEditDescription = canConnect
322
+ ? "Pair an agent first"
323
+ : canSend
324
+ ? "Send to the connected agent"
325
+ : "Edits made in the inspector";
326
+ const promptButton = (_jsxs(Button, { variant: "primary", className: "copy-prompt__main", "data-test": "copy-prompt", type: "button", disabled: disabled, "data-copied": copied ? "true" : "false", "data-agent-state": agent.state, "aria-busy": preparing || working || connecting ? "true" : undefined, onClick: hasMenu ? undefined : onClick, children: [icon, label, badgeCount > 0 ? (_jsx("span", { className: "copy-prompt__change-count", "data-test": "copy-prompt-change-count", children: badgeCount })) : null, hasMenu ? _jsx(IconChevronDown, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) : null] }));
327
+ return (_jsxs("div", { className: "copy-prompt__stack", "data-test": "copy-prompt-control", children: [hasMenu ? (_jsxs(Menu.Root, { modal: false, children: [_jsx(Menu.Trigger, { render: promptButton }), _jsx(Menu.Portal, { container: portalContainer(), children: _jsx(Menu.Positioner, { className: "inspector-popover__positioner", side: "bottom", align: "start", sideOffset: 6, children: _jsxs(Menu.Popup, { className: "inspector-popover__popup copy-prompt__menu", "data-test": "copy-prompt-menu", children: [activeArtifactId ? (_jsxs(_Fragment, { children: [_jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-prompt", label: "Copy iteration prompt", onClick: () => void copyIterationPrompt("iteration"), children: [_jsx(IconArtboard, { className: "copy-prompt__menu-icon", size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy iteration prompt" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Continue editing this HTML iteration." })] })] }), _jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-implementation", label: "Copy prompt for live app", onClick: () => void copyIterationPrompt("application"), children: [_jsx(IconBoltFilled, { className: "copy-prompt__menu-icon copy-prompt__menu-icon--live", size: "var(--icon-size-small)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy prompt for live app" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Implement design in real app" })] })] })] })) : (_jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-style-edit-prompt", label: styleEditLabel, disabled: !canConnect && changeCount === 0, onClick: () => void onClick(), children: [canConnect
328
+ ? _jsx(IconPlugConnected, { className: "copy-prompt__menu-icon", size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" })
329
+ : canSend
330
+ ? _jsx(IconSend, { className: "copy-prompt__menu-icon", size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" })
331
+ : _jsx(IconPencil, { className: "copy-prompt__menu-icon", size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: styleEditLabel }), _jsx("span", { className: "copy-prompt__menu-description", children: styleEditDescription })] })] })), visibleSketches.length > 0 ? _jsx(Menu.Separator, { className: "copy-prompt__menu-separator" }) : null, visibleSketches.map((item, index) => {
332
+ const busy = item.status === "dispatching" || item.status === "handing-off";
333
+ return (_jsxs("div", { className: "copy-prompt__menu-row", children: [_jsxs(Menu.Item, { className: "copy-prompt__menu-item copy-prompt__menu-item--sketch", "data-test": "copy-sketch", "data-sketch-id": item.document.id, label: `Copy Sketch ${index + 1}`, disabled: busy, onClick: () => void copySketchImageOnRequest(item.document.annotatedImage), children: [_jsx(SketchThumbnail, { item: item }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsxs("span", { className: "copy-prompt__menu-title", children: ["Copy Sketch ", index + 1] }), item.document.description ? (_jsx("span", { className: "copy-prompt__menu-description copy-prompt__menu-description--truncate", children: item.document.description })) : null] })] }), _jsx(IconButton, { className: "copy-prompt__menu-delete", variant: "quiet", size: "compact", label: `Delete Sketch ${index + 1}`, "data-test": "copy-sketch-delete", disabled: busy, onClick: () => void deleteSketch(item.document.id), children: _jsx(IconX, { size: "var(--icon-size-small)", "aria-hidden": "true" }) })] }, item.document.id));
334
+ })] }) }) })] }, activeDraftCardId)) : promptButton, statusAction || agentStatus.kind === "connected-not-listening" ? (_jsx(StatusCallout, { className: "copy-prompt__agent-status", tone: agentStatus.tone, "data-test": "agent-connection-status", children: _jsxs("div", { className: "copy-prompt__agent-status-content", children: [_jsx("span", { role: "status", children: agentStatus.label }), statusAction ? (_jsx(Button, { size: "compact", variant: "quiet", "data-test": "agent-status-action", "data-action": statusAction.kind, type: "button", onClick: () => void handleAgentStatusAction(), children: statusAction.label })) : null] }) })) : null, showImplemented ? (_jsxs("p", { className: "copy-prompt__hint copy-prompt__hint--centered copy-prompt__hint--implemented", "data-test": "handoff-implemented-hint", role: "status", children: ["Changes implemented.", remainingCount > 0 ? (_jsxs(_Fragment, { children: [" ", _jsx("button", { type: "button", className: "copy-prompt__hint-action", "data-test": "handoff-clear-remaining", onClick: clearRemaining, children: "Clear stale edits" })] })) : null] })) : null, saveMessage ? (_jsx("p", { className: "copy-prompt__hint copy-prompt__hint--centered", "data-test": "iteration-save-hint", role: "status", children: saveMessage })) : null, sketchFallback ? (_jsxs(StatusCallout, { className: "copy-prompt__sketch-fallback", tone: "warning", "data-test": "sketch-dispatch-error", children: [_jsx("span", { children: sketchFallback.error }), _jsx(Button, { size: "compact", variant: "secondary", type: "button", "data-test": "sketch-copy-fallback", onClick: () => {
335
+ void copyHandoff(sketchFallback.handoff).then(() => {
336
+ setSketchFallback(null);
337
+ }).catch(() => undefined);
290
338
  }, children: "Copy prompt" })] })) : null, _jsx(SettingsDialog, { open: settingsOpen, initialSection: settingsSection, value: customInstructions, onValueChange: handleCustomInstructionsChange, onOpenChange: setSettingsOpen, projectId: runtimeConfig.projectId, origin: window.location.origin, snapshot: agent, onConnect: () => agentClient.connect(), onTakeOver: () => agentClient.takeOver(), onDisconnect: () => agentClient.disconnect(), onCheckAgain: () => agentClient.checkConnection() })] }));
291
339
  }
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { IconArtboard, IconBoltFilled, IconSparkles } from "@tabler/icons-react";
3
2
  import { useCanvasPresentation } from "../canvas/viewStore.js";
4
3
  import { Disclosure } from "../ui/Disclosure.js";
5
- import gridIllustration from "../ui/assets/grid.svg";
4
+ import { CANVAS_ILLUSTRATION_SVG } from "../ui/assets/canvasIllustration.js";
5
+ import { GRID_ILLUSTRATION_SVG } from "../ui/assets/gridIllustration.js";
6
6
  import { getPlatformModifierKey, isMacPlatform } from "./shortcuts.js";
7
7
  export function EmptyState() {
8
8
  const presentation = useCanvasPresentation();
@@ -12,5 +12,5 @@ export function EmptyState() {
12
12
  const modifierName = macPlatform ? "Command" : "Control";
13
13
  const optionKey = macPlatform ? "⌥" : "Alt";
14
14
  const optionName = macPlatform ? "Option" : "Alt";
15
- return (_jsxs("div", { className: `empty-state${canvas ? " empty-state--canvas" : ""}`, "data-test": "empty-state", children: [canvas ? (_jsxs("div", { className: "empty-state__canvas", "data-test": "canvas-intro", children: [_jsx("h2", { className: "empty-state__title", children: "Using the canvas" }), _jsxs("p", { children: [_jsx("span", { className: "empty-state__live-badge", children: _jsx(IconBoltFilled, { "aria-hidden": "true" }) }), _jsxs("span", { children: [_jsx("strong", { children: "Live app" }), "A view of your real running app"] })] }), _jsxs("p", { children: [_jsx(IconArtboard, { "aria-hidden": "true" }), _jsxs("span", { children: [_jsx("strong", { children: "HTML iterations" }), "Unlinked from live app for isolated changes. Ask your agent to implement in live app once ready."] })] }), _jsxs("p", { children: [_jsx(IconSparkles, { "aria-hidden": "true" }), _jsxs("span", { children: [_jsx("strong", { children: "Agent with MCP" }), "Ask your agent to: lay out flows, show page states and create iterations."] })] })] })) : (_jsxs(_Fragment, { children: [_jsx("img", { className: "empty-state__icon", src: gridIllustration, alt: "", "aria-hidden": "true" }), _jsx("h2", { className: "empty-state__title", children: "Select an element to edit" })] })), _jsx(Disclosure, { className: "empty-state__shortcuts", title: "Keyboard shortcuts", triggerDataTest: "empty-state-shortcuts-toggle", defaultOpen: !canvas, children: _jsxs("ul", { className: "empty-state__shortcut-list", children: [_jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Nudge" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": "Arrow keys", children: [_jsx("kbd", { children: "\u2191" }), _jsx("kbd", { children: "\u2193" }), _jsx("kbd", { children: "\u2190" }), _jsx("kbd", { children: "\u2192" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Big Nudge (8px)" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": "Shift plus arrow keys", children: [_jsx("kbd", { children: "Shift" }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "Arrow" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Select deeper" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": `${modifierName} plus click`, children: [_jsx("kbd", { children: modifierKey }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "Click" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Measure" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": `${optionName} plus hover`, children: [_jsx("kbd", { children: optionKey }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "Hover" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Hide UI" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": `${modifierName} plus backslash`, children: [_jsx("kbd", { children: modifierKey }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "\\" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Hold to view original" }), _jsx("span", { className: "empty-state__shortcut-keys", "aria-label": "Backslash", children: _jsx("kbd", { children: "\\" }) })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Undo" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": `${modifierName} plus Z`, children: [_jsx("kbd", { children: modifierKey }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "Z" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Deselect" }), _jsx("span", { className: "empty-state__shortcut-keys", children: _jsx("kbd", { children: "Esc" }) })] })] }) }, presentation)] }));
15
+ return (_jsxs("div", { className: "empty-state", "data-test": "empty-state", children: [canvas ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "empty-state__icon", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: CANVAS_ILLUSTRATION_SVG } }), _jsxs("p", { className: "empty-state__hint", "data-test": "canvas-intro", children: ["Hold ", _jsx("kbd", { children: "A" }), " to click links and open them as new pages on the canvas."] })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "empty-state__icon", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: GRID_ILLUSTRATION_SVG } }), _jsx("h2", { className: "empty-state__title", children: "Select an element to edit" })] })), _jsx(Disclosure, { className: "empty-state__shortcuts", title: "Keyboard shortcuts", triggerDataTest: "empty-state-shortcuts-toggle", defaultOpen: true, children: _jsxs("ul", { className: "empty-state__shortcut-list", children: [_jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Nudge" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": "Arrow keys", children: [_jsx("kbd", { children: "\u2191" }), _jsx("kbd", { children: "\u2193" }), _jsx("kbd", { children: "\u2190" }), _jsx("kbd", { children: "\u2192" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Big Nudge (8px)" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": "Shift plus arrow keys", children: [_jsx("kbd", { children: "Shift" }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "Arrow" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Select deeper" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": `${modifierName} plus click`, children: [_jsx("kbd", { children: modifierKey }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "Click" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Measure" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": `${optionName} plus hover`, children: [_jsx("kbd", { children: optionKey }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "Hover" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Hide UI" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": `${modifierName} plus backslash`, children: [_jsx("kbd", { children: modifierKey }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "\\" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Hold to view original" }), _jsx("span", { className: "empty-state__shortcut-keys", "aria-label": "Backslash", children: _jsx("kbd", { children: "\\" }) })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Undo" }), _jsxs("span", { className: "empty-state__shortcut-keys", "aria-label": `${modifierName} plus Z`, children: [_jsx("kbd", { children: modifierKey }), _jsx("span", { "aria-hidden": "true", children: "+" }), _jsx("kbd", { children: "Z" })] })] }), _jsxs("li", { className: "empty-state__shortcut", "data-test": "empty-state-shortcut", children: [_jsx("span", { children: "Deselect" }), _jsx("span", { className: "empty-state__shortcut-keys", children: _jsx("kbd", { children: "Esc" }) })] })] }) }, presentation)] }));
16
16
  }
@@ -1,10 +1,7 @@
1
1
  import type { ReactElement } from "react";
2
- import { toggleInspector, setInspectorOpen } from "./openStore.js";
3
2
  declare global {
4
3
  interface Window {
5
4
  MutationObserver: typeof MutationObserver;
6
5
  }
7
6
  }
8
- export { toggleInspector, setInspectorOpen };
9
- export type { SelectedElement } from "../selection/selectionStore.js";
10
7
  export declare function InspectorShell(): ReactElement;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useMemo, useState } from "react";
2
+ import { useEffect, useMemo, useState, useSyncExternalStore } from "react";
3
3
  import { IconLayoutSidebarRight } from "@tabler/icons-react";
4
- import { useInspectorOpen, toggleInspector, setInspectorOpen } from "./openStore.js";
4
+ import { useInspectorOpen, setInspectorOpen } from "./openStore.js";
5
5
  import { useSelectedElement, useSelectedElements, setSelectedElement, removeSelectedElement, } from "../selection/selectionStore.js";
6
6
  import { findTokenRow } from "../styleEditors/rowLookup.js";
7
7
  import { useBrowserCssInspection } from "../inspection/useBrowserCssInspection.js";
@@ -17,7 +17,7 @@ import { AppearanceSection } from "../styleEditors/AppearanceSection.js";
17
17
  import { BoxShadowEditor } from "../styleEditors/BoxShadowEditor.js";
18
18
  import { LayoutSection } from "../styleEditors/LayoutSection.js";
19
19
  import { ChangesLog } from "./ChangesLog.js";
20
- import { discardChangesForInstanceOverride, undo, redo } from "../changes/changesLog.js";
20
+ import { discardChangesForInstanceOverride, undo, redo } from "../drafts/store.js";
21
21
  import { countSourceSiteMatches, getEditScope, relinkElement, sourceSiteSelector, unlinkElement } from "../selection/editScope.js";
22
22
  import { canEditStyles } from "../tokens/editActions.js";
23
23
  import { Button } from "../ui/Button.js";
@@ -27,6 +27,7 @@ import { StatusCallout } from "../ui/StatusCallout.js";
27
27
  import { IconButton } from "../ui/IconButton.js";
28
28
  import { UI_STYLES } from "../ui/styles.js";
29
29
  import { getActiveStyleState, setActiveStyleState } from "./styleState.js";
30
+ import { useInteractionStatePreview } from "./interactionStatePreview.js";
30
31
  import { getInspectorToggleShortcutLabel, isEditableEvent } from "./shortcuts.js";
31
32
  import { formatInspectorLabel } from "../ui/labels.js";
32
33
  import { FieldRow } from "../ui/FieldRow.js";
@@ -34,6 +35,8 @@ import { Select } from "../ui/Select.js";
34
35
  import { clearSelectedFrameChanges } from "../canvas/sessionStore.js";
35
36
  import { useFocusedCardId, useSelectedCardId, } from "../canvas/canvasStore.js";
36
37
  import { getActiveCanvasFrame } from "../canvas/activeCanvasDocument.js";
38
+ import { canvasFrameHost } from "../canvas/canvasFrame.js";
39
+ import { getFrameRegistryVersion, subscribeFrameRegistry } from "../canvas/projection.js";
37
40
  import { getElementWindow } from "../runtime/domRealm.js";
38
41
  import { deleteElement, nudgeElement } from "../overlay/structuralGestures.js";
39
42
  import { AtRuleContextProvider } from "../ui/AtRuleContext.js";
@@ -45,10 +48,11 @@ import { EmptyState } from "./EmptyState.js";
45
48
  import { createStyleSelection } from "../selection/styleSelection.js";
46
49
  import { intersectTokenEntries } from "../inspection/selectionProperty.js";
47
50
  import { isNudgeUiDev } from "../runtime/devFlag.js";
48
- import { isDemoRuntime } from "../runtime/runtimeConfig.js";
51
+ import { isDemoRuntime, isNudgeUiFeatureEnabled } from "../runtime/runtimeConfig.js";
49
52
  import { SketchWorkspace } from "../sketch/SketchWorkspace.js";
50
53
  import { useSketchInteractionActive } from "../sketch/interaction.js";
51
54
  import { Tooltip } from "../ui/Tooltip.js";
55
+ import { InspectorStyle } from "../ui/InspectorStyle.js";
52
56
  function findFirstTokenRow(rows, properties) {
53
57
  for (const property of properties) {
54
58
  const row = findTokenRow(rows, property);
@@ -62,7 +66,6 @@ function findFirstTokenRow(rows, properties) {
62
66
  }
63
67
  return null;
64
68
  }
65
- export { toggleInspector, setInspectorOpen };
66
69
  function nodeMatchesSelector(node, selector) {
67
70
  if (!selector || node.nodeType !== node.ELEMENT_NODE)
68
71
  return false;
@@ -88,11 +91,14 @@ function scopeMutationAffectsSelection(records, selected) {
88
91
  export function InspectorShell() {
89
92
  const isOpen = useInspectorOpen();
90
93
  const runtimeConfig = useNudgeUiRuntimeConfig();
91
- const sketchEnabled = isNudgeUiDev() && !isDemoRuntime();
94
+ const sketchEnabled = isNudgeUiDev()
95
+ && !runtimeConfig.inspectionUnavailableReason
96
+ && (!isDemoRuntime() || isNudgeUiFeatureEnabled(runtimeConfig.features, "sketch"));
92
97
  const sketchActive = useSketchInteractionActive();
93
98
  const selectedCardId = useSelectedCardId();
94
99
  const focusedCardId = useFocusedCardId();
95
- const sketchHost = selectedCardId || focusedCardId ? getActiveCanvasFrame() : null;
100
+ useSyncExternalStore(subscribeFrameRegistry, getFrameRegistryVersion);
101
+ const sketchHost = selectedCardId || focusedCardId ? canvasFrameHost(getActiveCanvasFrame()) : null;
96
102
  const selected = useSelectedElement();
97
103
  const selectedElements = useSelectedElements();
98
104
  const inlineTextFeedback = getInlineTextFeedback(useInlineTextDiagnostic());
@@ -100,6 +106,7 @@ export function InspectorShell() {
100
106
  const [settingsSection, setSettingsSection] = useState("instructions");
101
107
  const [scopeRevision, refreshScope] = useState(0);
102
108
  const [styleState, setStyleState] = useState(getActiveStyleState());
109
+ useInteractionStatePreview(selectedElements.length === 1 ? selectedElements[0].domElement : null, styleState);
103
110
  const cssInspection = useBrowserCssInspection(selectedElements, styleState);
104
111
  const isMultiSelection = selectedElements.length > 1;
105
112
  useEffect(() => {
@@ -144,7 +151,7 @@ export function InspectorShell() {
144
151
  return () => observers.forEach((observer) => observer.disconnect());
145
152
  }, [selectedElements, scopeRevision]);
146
153
  useEffect(() => {
147
- if (sketchActive)
154
+ if (sketchActive || runtimeConfig.inspectionUnavailableReason)
148
155
  return;
149
156
  function onKeydown(event) {
150
157
  if (isInlineTextEditingActive())
@@ -197,7 +204,7 @@ export function InspectorShell() {
197
204
  }
198
205
  window.addEventListener("keydown", onKeydown);
199
206
  return () => window.removeEventListener("keydown", onKeydown);
200
- }, [isMultiSelection, selected, sketchActive]);
207
+ }, [isMultiSelection, selected, sketchActive, runtimeConfig.inspectionUnavailableReason]);
201
208
  const inspectionSnapshot = cssInspection.element;
202
209
  const styleSelection = useMemo(() => createStyleSelection(selectedElements, cssInspection.elements, selected), [cssInspection.elements, selected, selectedElements]);
203
210
  const selectionEditable = useMemo(() => styleSelection ? canEditStyles(styleSelection.target) : false, [scopeRevision, styleSelection]);
@@ -205,7 +212,7 @@ export function InspectorShell() {
205
212
  const tokenRows = useMemo(() => styleSelection ? [...styleSelection.primaryRows] : [], [styleSelection]);
206
213
  const availableInteractionStates = inspectionSnapshot?.availableStates ?? [];
207
214
  const showText = selectedElements.length > 0 && selectedElements.every((element, index) => isTextRelevant(element.domElement, cssInspection.elements[index]?.properties ?? []));
208
- const showInteractionState = !isMultiSelection && availableInteractionStates.length > 2;
215
+ const showInteractionState = !isMultiSelection && availableInteractionStates.length > 1;
209
216
  const paintedBackgroundRow = findFirstTokenRow(tokenRows, ["background-color", "background"]);
210
217
  const backgroundTokenRow = useMemo(() => {
211
218
  if (!selected)
@@ -230,12 +237,13 @@ export function InspectorShell() {
230
237
  setSettingsOpen(true);
231
238
  }
232
239
  function clearInspectorSession() {
233
- clearSelectedFrameChanges();
240
+ // Sketch persistence failures are reported by the sketch store in the panel.
241
+ void clearSelectedFrameChanges().catch(() => undefined);
234
242
  }
235
- return (_jsxs(_Fragment, { children: [_jsx("style", { "data-test": "inspector-styles", children: UI_STYLES }), _jsxs("div", { className: "panel", "data-open": isOpen ? "true" : "false", children: [_jsxs("div", { className: "panel__tabs", "aria-label": "Inspector controls", children: [_jsxs("div", { className: "panel__header-row", "data-test": "inspect-tab", children: [_jsx(Tooltip, { content: "Hide inspector", shortcut: getInspectorToggleShortcutLabel(), stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", label: "Collapse inspector", "data-test": "collapse-inspector", disabled: sketchActive, style: { marginLeft: "calc(var(--space-8) * -1)" }, onClick: () => {
243
+ return (_jsxs(_Fragment, { children: [_jsx(InspectorStyle, { testId: "inspector-styles", css: UI_STYLES }), _jsxs("div", { className: "panel", "data-open": isOpen ? "true" : "false", children: [_jsxs("div", { className: "panel__tabs", "aria-label": "Inspector controls", children: [_jsxs("div", { className: "panel__header-row", "data-test": "inspect-tab", children: [_jsx(Tooltip, { content: "Hide inspector", shortcut: getInspectorToggleShortcutLabel(), stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", label: "Collapse inspector", "data-test": "collapse-inspector", disabled: sketchActive, style: { marginLeft: "calc(var(--space-8) * -1)" }, onClick: () => {
236
244
  cancelInlineTextEdit();
237
245
  setInspectorOpen(false);
238
- }, children: _jsx(IconLayoutSidebarRight, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) }) }), _jsxs("div", { className: "panel__header-actions", children: [_jsx(PeekOriginalButton, {}), _jsx(AgentStatusButton, { onOpenSettings: openSettings })] })] }), _jsx("div", { className: "panel__copy-row", children: _jsx(CopyPromptButton, { settingsOpen: settingsOpen, settingsSection: settingsSection, onOpenSettings: openSettings, onSettingsOpenChange: setSettingsOpen }) })] }), _jsxs("div", { className: "panel__body", children: [inlineTextFeedback ? (_jsx(StatusCallout, { tone: "danger", className: "inline-text-diagnostic", "data-test": "inline-text-diagnostic", role: "status", "aria-live": "polite", "aria-atomic": "true", children: _jsx("span", { children: inlineTextFeedback.message }) })) : null, selected ? (_jsxs(_Fragment, { children: [isMultiSelection || showInteractionState || hasEditScopeCallout ? (_jsxs("div", { className: `selection${hasEditScopeCallout ? "" : " selection--without-scope-callout"}`, "data-test": "selection", "data-selected-cid": selected.cid, "data-selected-src": selected.src, "data-selected-count": selectedElements.length, children: [isMultiSelection ? (_jsx("div", { className: "selection__summary", "data-test": "multi-selection-summary", children: _jsxs("span", { className: "selection__count", children: [selectedElements.length, " elements selected"] }) })) : null, hasEditScopeCallout ? (_jsx(StatusCallout, { tone: editScope === "rendered-instance" ? "neutral" : "accent", "data-test": "edit-scope", "data-lost": "false", children: editScope === "rendered-instance" ? (_jsxs("div", { className: "scope__unlinked", children: [_jsx("span", { children: "Element unlinked" }), _jsx(Button, { size: "compact", className: "scope__action", "data-test": "relink-element", onClick: () => {
246
+ }, children: _jsx(IconLayoutSidebarRight, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) }) }), _jsxs("div", { className: "panel__header-actions", children: [_jsx(PeekOriginalButton, {}), _jsx(AgentStatusButton, { onOpenSettings: openSettings })] })] }), _jsx("div", { className: "panel__copy-row", children: _jsx(CopyPromptButton, { settingsOpen: settingsOpen, settingsSection: settingsSection, onOpenSettings: openSettings, onSettingsOpenChange: setSettingsOpen }) })] }), _jsxs("div", { className: "panel__body", children: [inlineTextFeedback ? (_jsx(StatusCallout, { tone: "danger", className: "inline-text-diagnostic", "data-test": "inline-text-diagnostic", role: "status", "aria-live": "polite", "aria-atomic": "true", children: _jsx("span", { children: inlineTextFeedback.message }) })) : null, runtimeConfig.inspectionUnavailableReason ? (_jsxs(StatusCallout, { tone: "danger", "data-test": "page-inspection-error", role: "status", children: ["Can\u2019t load Nudge on this page due to ", runtimeConfig.inspectionUnavailableReason, "."] })) : selected ? (_jsxs(_Fragment, { children: [showInteractionState || hasEditScopeCallout ? (_jsxs("div", { className: `selection${hasEditScopeCallout ? "" : " selection--without-scope-callout"}`, "data-test": "selection", "data-selected-cid": selected.cid, "data-selected-src": selected.src, "data-selected-count": selectedElements.length, children: [hasEditScopeCallout ? (_jsx(StatusCallout, { tone: editScope === "rendered-instance" ? "neutral" : "accent", "data-test": "edit-scope", "data-lost": "false", children: editScope === "rendered-instance" ? (_jsxs("div", { className: "scope__unlinked", children: [_jsx("span", { children: "Element unlinked" }), _jsx(Button, { size: "compact", className: "scope__action", "data-test": "relink-element", onClick: () => {
239
247
  const overrideId = relinkElement(selected.domElement);
240
248
  if (overrideId)
241
249
  discardChangesForInstanceOverride(overrideId);
@@ -3,11 +3,11 @@ import { useEffect, useSyncExternalStore } from "react";
3
3
  import { IconButton } from "../ui/IconButton.js";
4
4
  import { Tooltip } from "../ui/Tooltip.js";
5
5
  import { PeekOriginalIcon } from "./PeekOriginalIcon.js";
6
- import { draftChangeStore } from "../changes/draftChanges.js";
6
+ import { getActiveDraftChanges, subscribeActiveDraftChanges } from "../drafts/store.js";
7
7
  import { setOriginalPreviewActive, useOriginalPreviewActive, } from "./originalPreview.js";
8
8
  function useDraftChangeCount() {
9
- return useSyncExternalStore(draftChangeStore.subscribe, () => {
10
- const snapshot = draftChangeStore.getSnapshot();
9
+ return useSyncExternalStore(subscribeActiveDraftChanges, () => {
10
+ const snapshot = getActiveDraftChanges();
11
11
  return snapshot.changes.length + snapshot.structuralChanges.length;
12
12
  }, () => 0);
13
13
  }
@@ -0,0 +1,9 @@
1
+ import { type InteractionState } from "./styleState.js";
2
+ /** Matches native interaction states and the Inspector's temporary CSS state. */
3
+ export declare function selectorForInteractionStatePreview(selector: string): string;
4
+ /**
5
+ * Projects CSS interaction states into the selected element's document.
6
+ * This previews styles without dispatching events or changing application state.
7
+ */
8
+ export declare function startInteractionStatePreview(element: HTMLElement, state: InteractionState): () => void;
9
+ export declare function useInteractionStatePreview(element: HTMLElement | null, state: InteractionState): void;