nudge-ui 0.2.2 → 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 (226) hide show
  1. package/README.md +164 -46
  2. package/dist/client.mjs +252 -61
  3. package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
  4. package/dist/css/value-semantics/colorSemantics.js +17 -0
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
  6. package/dist/hosts/astro/clientTransport.js +38 -32
  7. package/dist/hosts/astro/integration.js +5 -1
  8. package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
  9. package/dist/hosts/astro/markdownIdentity.js +81 -0
  10. package/dist/hosts/next/manifest.js +1 -1
  11. package/dist/hosts/next/sidecar.js +5 -1
  12. package/dist/hosts/static/manifest.js +1 -1
  13. package/dist/hosts/static/server.js +3 -0
  14. package/dist/hosts/vite/vite.d.ts +3 -0
  15. package/dist/hosts/vite/vite.js +24 -4
  16. package/dist/inspector/agent/McpConnectionDialog.js +13 -19
  17. package/dist/inspector/agent/activity.d.ts +18 -0
  18. package/dist/inspector/agent/activity.js +70 -0
  19. package/dist/inspector/agent/client.d.ts +2 -0
  20. package/dist/inspector/agent/client.js +29 -1
  21. package/dist/inspector/agent/connectionStatus.js +1 -1
  22. package/dist/inspector/agent/httpTransport.js +9 -2
  23. package/dist/inspector/agent/protocol.d.ts +6 -3
  24. package/dist/inspector/agent/protocol.js +1 -1
  25. package/dist/inspector/agent/verification.d.ts +11 -1
  26. package/dist/inspector/agent/verification.js +53 -25
  27. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
  28. package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
  29. package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
  30. package/dist/inspector/canvas/CanvasCard.js +133 -38
  31. package/dist/inspector/canvas/CanvasElementOverlay.js +152 -16
  32. package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
  33. package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
  34. package/dist/inspector/canvas/CanvasToolbar.js +8 -4
  35. package/dist/inspector/canvas/CanvasWorkspace.js +225 -57
  36. package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
  37. package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
  38. package/dist/inspector/canvas/agentPresentation.js +64 -52
  39. package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
  40. package/dist/inspector/canvas/canvasGestures.js +1 -1
  41. package/dist/inspector/canvas/canvasStore.d.ts +46 -82
  42. package/dist/inspector/canvas/canvasStore.js +251 -284
  43. package/dist/inspector/canvas/frameContent.d.ts +21 -0
  44. package/dist/inspector/canvas/frameContent.js +44 -0
  45. package/dist/inspector/canvas/frameProtocol.d.ts +21 -4
  46. package/dist/inspector/canvas/frameProtocol.js +3 -3
  47. package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -1
  48. package/dist/inspector/canvas/keyboardShortcuts.js +4 -4
  49. package/dist/inspector/canvas/layout.d.ts +52 -0
  50. package/dist/inspector/canvas/layout.js +106 -0
  51. package/dist/inspector/canvas/projection.d.ts +15 -5
  52. package/dist/inspector/canvas/projection.js +69 -13
  53. package/dist/inspector/canvas/rendererBootstrap.js +32 -4
  54. package/dist/inspector/canvas/rendererElementSelector.js +47 -6
  55. package/dist/inspector/canvas/rendererStylesheet.js +2 -2
  56. package/dist/inspector/canvas/resizeHandleScale.js +1 -1
  57. package/dist/inspector/canvas/sessionStore.d.ts +11 -26
  58. package/dist/inspector/canvas/sessionStore.js +43 -183
  59. package/dist/inspector/canvas/staleChangeDetector.js +9 -3
  60. package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
  61. package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
  62. package/dist/inspector/canvas/viewStore.d.ts +38 -0
  63. package/dist/inspector/canvas/viewStore.js +104 -0
  64. package/dist/inspector/canvas/wheelPan.d.ts +8 -0
  65. package/dist/inspector/canvas/wheelPan.js +8 -0
  66. package/dist/inspector/changes/changesLog.d.ts +6 -5
  67. package/dist/inspector/changes/changesLog.js +40 -32
  68. package/dist/inspector/changes/draftChanges.d.ts +56 -0
  69. package/dist/inspector/changes/draftChanges.js +300 -0
  70. package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
  71. package/dist/inspector/changes/previewDiagnostics.js +11 -25
  72. package/dist/inspector/clientManifest.d.ts +1 -1
  73. package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
  74. package/dist/inspector/comments/CommentsOverlay.js +250 -0
  75. package/dist/inspector/comments/element.d.ts +6 -0
  76. package/dist/inspector/comments/element.js +47 -0
  77. package/dist/inspector/comments/store.d.ts +29 -0
  78. package/dist/inspector/comments/store.js +121 -0
  79. package/dist/inspector/comments/verification.d.ts +5 -0
  80. package/dist/inspector/comments/verification.js +52 -0
  81. package/dist/inspector/drafts/model.d.ts +35 -0
  82. package/dist/inspector/drafts/model.js +45 -0
  83. package/dist/inspector/drafts/store.d.ts +28 -0
  84. package/dist/inspector/drafts/store.js +231 -0
  85. package/dist/inspector/index.js +20 -27
  86. package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
  87. package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
  88. package/dist/inspector/iterations/capture.d.ts +2 -0
  89. package/dist/inspector/iterations/capture.js +173 -0
  90. package/dist/inspector/iterations/client.d.ts +7 -0
  91. package/dist/inspector/iterations/client.js +52 -0
  92. package/dist/inspector/iterations/coordinator.d.ts +15 -0
  93. package/dist/inspector/iterations/coordinator.js +130 -0
  94. package/dist/inspector/iterations/prompt.d.ts +3 -0
  95. package/dist/inspector/iterations/prompt.js +26 -0
  96. package/dist/inspector/overlay/DomNavigator.js +21 -15
  97. package/dist/inspector/overlay/interactionStyles.js +3 -1
  98. package/dist/inspector/overlay/spacingGestures.d.ts +13 -1
  99. package/dist/inspector/overlay/spacingGestures.js +82 -5
  100. package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
  101. package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
  102. package/dist/inspector/projection/renderedInstance.d.ts +2 -1
  103. package/dist/inspector/projection/renderedInstance.js +9 -5
  104. package/dist/inspector/projection/structuralProjection.d.ts +2 -1
  105. package/dist/inspector/projection/structuralProjection.js +19 -15
  106. package/dist/inspector/projection/textProjection.d.ts +2 -1
  107. package/dist/inspector/projection/textProjection.js +8 -4
  108. package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
  109. package/dist/inspector/prompt/clipboardHandoff.d.ts +10 -18
  110. package/dist/inspector/prompt/clipboardHandoff.js +92 -143
  111. package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
  112. package/dist/inspector/prompt/generatePrompt.js +17 -16
  113. package/dist/inspector/runtime/runtimeConfig.d.ts +9 -0
  114. package/dist/inspector/runtime/runtimeConfig.js +24 -0
  115. package/dist/inspector/settings/SettingsDialog.js +21 -8
  116. package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
  117. package/dist/inspector/shell/AgentStatusButton.js +36 -0
  118. package/dist/inspector/shell/AppShell.js +4 -2
  119. package/dist/inspector/shell/ChangesLog.js +46 -26
  120. package/dist/inspector/shell/CopyPromptButton.js +138 -89
  121. package/dist/inspector/shell/EmptyState.js +9 -8
  122. package/dist/inspector/shell/InspectorShell.js +13 -21
  123. package/dist/inspector/shell/PeekOriginalButton.js +24 -23
  124. package/dist/inspector/shell/originalPreview.js +5 -5
  125. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  126. package/dist/inspector/shell/shortcuts.js +11 -0
  127. package/dist/inspector/sketch/SketchEditor.js +14 -14
  128. package/dist/inspector/sketch/SketchEntryButton.js +1 -1
  129. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
  130. package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
  131. package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
  132. package/dist/inspector/sketch/SketchOverlay.js +7 -7
  133. package/dist/inspector/sketch/SketchWorkspace.js +18 -12
  134. package/dist/inspector/sketch/interaction.d.ts +1 -0
  135. package/dist/inspector/sketch/interaction.js +8 -4
  136. package/dist/inspector/sketch/model.d.ts +5 -0
  137. package/dist/inspector/sketch/model.js +6 -2
  138. package/dist/inspector/sketch/raster.d.ts +1 -2
  139. package/dist/inspector/sketch/raster.js +68 -4
  140. package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
  141. package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
  142. package/dist/inspector/styleEditors/BorderEditor.js +49 -78
  143. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
  144. package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
  145. package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
  146. package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
  147. package/dist/inspector/styleEditors/ColorPicker.js +10 -4
  148. package/dist/inspector/styleEditors/GapField.js +3 -5
  149. package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
  150. package/dist/inspector/styleEditors/GridChildSection.js +18 -9
  151. package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
  152. package/dist/inspector/styleEditors/GridPicker.js +8 -2
  153. package/dist/inspector/styleEditors/GridSection.js +1 -1
  154. package/dist/inspector/styleEditors/GridValueField.js +19 -14
  155. package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
  156. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
  157. package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
  158. package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
  159. package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
  160. package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
  161. package/dist/inspector/styleEditors/LayoutSection.js +32 -11
  162. package/dist/inspector/styleEditors/SpacingBox.js +7 -5
  163. package/dist/inspector/styleEditors/Typography.js +11 -3
  164. package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
  165. package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
  166. package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
  167. package/dist/inspector/styleEditors/gradientValue.js +114 -0
  168. package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
  169. package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
  170. package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
  171. package/dist/inspector/styleEditors/layoutValue.js +0 -42
  172. package/dist/inspector/styleEditors/nudgeValue.d.ts +2 -2
  173. package/dist/inspector/styleEditors/nudgeValue.js +36 -8
  174. package/dist/inspector/tokens/TokenField.d.ts +7 -0
  175. package/dist/inspector/tokens/TokenField.js +29 -21
  176. package/dist/inspector/tokens/TokenLabel.js +5 -14
  177. package/dist/inspector/tokens/editActions.d.ts +1 -1
  178. package/dist/inspector/tokens/editActions.js +1 -1
  179. package/dist/inspector/ui/AtRuleContext.js +4 -6
  180. package/dist/inspector/ui/Button.d.ts +6 -2
  181. package/dist/inspector/ui/Button.js +4 -3
  182. package/dist/inspector/ui/ControlSurface.d.ts +2 -2
  183. package/dist/inspector/ui/ControlSurface.js +4 -3
  184. package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
  185. package/dist/inspector/ui/InlineStyleWarning.js +13 -0
  186. package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
  187. package/dist/inspector/ui/InspectorPopover.js +4 -5
  188. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  189. package/dist/inspector/ui/PopoverListbox.js +8 -7
  190. package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
  191. package/dist/inspector/ui/SegmentedControl.js +2 -2
  192. package/dist/inspector/ui/Select.d.ts +6 -1
  193. package/dist/inspector/ui/Select.js +7 -11
  194. package/dist/inspector/ui/SideValuesField.js +1 -1
  195. package/dist/inspector/ui/Tooltip.d.ts +26 -0
  196. package/dist/inspector/ui/Tooltip.js +32 -0
  197. package/dist/inspector/ui/classNames.d.ts +2 -0
  198. package/dist/inspector/ui/classNames.js +4 -0
  199. package/dist/inspector/ui/customCursors.d.ts +23 -0
  200. package/dist/inspector/ui/customCursors.js +122 -0
  201. package/dist/inspector/ui/dragCursor.d.ts +0 -2
  202. package/dist/inspector/ui/dragCursor.js +2 -10
  203. package/dist/inspector/ui/portalContainer.d.ts +2 -0
  204. package/dist/inspector/ui/portalContainer.js +6 -0
  205. package/dist/inspector/ui/styles.js +29 -21
  206. package/dist/inspector/workspace/commands.d.ts +9 -0
  207. package/dist/inspector/workspace/commands.js +82 -0
  208. package/dist/inspector/workspace/controller.d.ts +4 -0
  209. package/dist/inspector/workspace/controller.js +66 -0
  210. package/dist/inspector/workspace/handoff.d.ts +42 -0
  211. package/dist/inspector/workspace/handoff.js +115 -0
  212. package/dist/inspector/workspace/timeline.d.ts +30 -0
  213. package/dist/inspector/workspace/timeline.js +108 -0
  214. package/dist/internal/agent-protocol.d.ts +19 -2
  215. package/dist/internal/agent-protocol.js +13 -0
  216. package/dist/nudge-ui.mjs +558 -311
  217. package/dist/project/artifacts.d.ts +13 -0
  218. package/dist/project/artifacts.js +253 -0
  219. package/dist/transport/artifacts.d.ts +8 -0
  220. package/dist/transport/artifacts.js +9 -0
  221. package/dist/transport/editor.js +3 -1
  222. package/dist/transport/routes.d.ts +2 -0
  223. package/dist/transport/routes.js +2 -0
  224. package/package.json +6 -2
  225. package/dist/inspector/changes/workspaceChanges.d.ts +0 -47
  226. package/dist/inspector/changes/workspaceChanges.js +0 -265
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { Dialog } from "@base-ui/react/dialog";
4
- import { IconColorSwatch, IconPlugConnected, IconSettings, IconX, } from "@tabler/icons-react";
4
+ import { IconColorSwatch, IconPlugConnected, IconSettings, IconX, IconTrash, } from "@tabler/icons-react";
5
5
  import { useBrowserCssInspection } from "../inspection/useBrowserCssInspection.js";
6
6
  import { McpConnectionContent, } from "../agent/McpConnectionDialog.js";
7
7
  import { PromptSettingsFields, } from "../prompt/PromptSettingsDialog.js";
@@ -10,10 +10,17 @@ import { useSelectedElement } from "../selection/selectionStore.js";
10
10
  import { useFocusedCardId, useSelectedCardId } from "../canvas/canvasStore.js";
11
11
  import { getRegisteredFrames } from "../canvas/projection.js";
12
12
  import { getBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
13
+ import { portalContainer } from "../ui/portalContainer.js";
14
+ import { clearRestoreCount, clearSession } from "../canvas/sessionStore.js";
15
+ import { hasWriteLease } from "../canvas/workspaceLease.js";
16
+ import { cancelSketchInteraction } from "../sketch/interaction.js";
17
+ import { closeSketchNote } from "../sketch/sketchNote.js";
18
+ import { clearSketchClipboardHandoff } from "../sketch/handoff.js";
19
+ import { clearSketchesForProject } from "../sketch/store.js";
13
20
  const SETTINGS_SECTIONS = [
14
21
  {
15
22
  id: "instructions",
16
- label: "Custom instructions",
23
+ label: "Instructions",
17
24
  icon: _jsx(IconSettings, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }),
18
25
  },
19
26
  {
@@ -27,11 +34,6 @@ const SETTINGS_SECTIONS = [
27
34
  icon: _jsx(IconColorSwatch, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }),
28
35
  },
29
36
  ];
30
- function portalContainer() {
31
- return typeof document !== "undefined"
32
- ? document.getElementById("nudge-ui-root")?.shadowRoot ?? document.body
33
- : null;
34
- }
35
37
  /** Provides one settings surface for prompt, MCP, and token configuration. */
36
38
  export function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onTakeOver, onDisconnect, onCheckAgain, }) {
37
39
  const [activeSection, setActiveSection] = useState(initialSection);
@@ -52,5 +54,16 @@ export function SettingsDialog({ open, initialSection, value, onValueChange, onO
52
54
  if (open)
53
55
  setActiveSection(initialSection);
54
56
  }, [initialSection, open]);
55
- return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { className: "settings__backdrop", "data-test": "settings-backdrop" }), _jsxs(Dialog.Popup, { className: "settings__popup", "data-test": "settings-dialog", initialFocus: closeRef, children: [_jsx(Dialog.Title, { className: "settings__visually-hidden", children: "Settings" }), _jsx(Dialog.Description, { className: "settings__visually-hidden", children: "Configure how Nudge connects to your coding agent and uses your design system." }), _jsx(Dialog.Close, { ref: closeRef, className: "icon-button icon-button--quiet settings__close", "aria-label": "Close settings", "data-test": "settings-close", type: "button", children: _jsx(IconX, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) }), _jsxs("div", { className: "settings__body", children: [_jsxs("nav", { className: "settings__nav", "aria-label": "Settings sections", children: [_jsx("span", { className: "settings__nav-label", children: "Settings" }), _jsx("div", { className: "settings__nav-items", children: SETTINGS_SECTIONS.map((section) => (_jsxs("button", { className: `settings__nav-item${activeSection === section.id ? " settings__nav-item--active" : ""}`, "data-test": `settings-nav-${section.id}`, type: "button", "aria-current": activeSection === section.id ? "page" : undefined, onClick: () => setActiveSection(section.id), children: [section.icon, _jsx("span", { className: "settings__nav-item-label", children: section.label })] }, section.id))) })] }), _jsxs("main", { className: "settings__content", "data-test": "settings-content", children: [activeSection === "instructions" ? (_jsxs("section", { className: "settings__section", "data-test": "prompt-settings-dialog", "data-settings-section": "instructions", children: [_jsx("h2", { className: "settings__section-title", children: activeDefinition.label }), _jsx("p", { className: "settings__section-description", children: "These instructions are added to the end of every copied prompt." }), _jsx(PromptSettingsFields, { value: value, onChange: onValueChange, instructionsRef: instructionsRef })] })) : null, activeSection === "mcp" ? (_jsxs("section", { className: "settings__section", "data-test": "settings-section-mcp", children: [_jsx("h2", { className: "settings__section-title", children: activeDefinition.label }), _jsx("div", { "data-test": "mcp-connection-dialog", children: _jsx(McpConnectionContent, { projectId: projectId, origin: origin, snapshot: snapshot, onConnect: onConnect, onTakeOver: onTakeOver, onDisconnect: onDisconnect, onCheckAgain: onCheckAgain }) })] })) : null, activeSection === "tokens" ? (_jsxs("section", { className: "settings__section", "data-test": "settings-section-tokens", children: [_jsx("h2", { className: "settings__section-title", children: activeDefinition.label }), _jsx("p", { className: "settings__section-description", children: "Inspect and edit the design tokens available in this page." }), _jsx(TokensPanel, { rows: tokenRows })] })) : null] })] })] })] }) }));
57
+ function resetCanvas() {
58
+ if (!hasWriteLease())
59
+ return;
60
+ cancelSketchInteraction();
61
+ closeSketchNote();
62
+ clearSketchClipboardHandoff();
63
+ clearSession();
64
+ clearRestoreCount();
65
+ void clearSketchesForProject(projectId).catch(() => undefined);
66
+ onOpenChange(false);
67
+ }
68
+ return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { className: "settings__backdrop", "data-test": "settings-backdrop" }), _jsxs(Dialog.Popup, { className: "settings__popup", "data-test": "settings-dialog", initialFocus: closeRef, children: [_jsx(Dialog.Title, { className: "settings__visually-hidden", children: "Settings" }), _jsx(Dialog.Description, { className: "settings__visually-hidden", children: "Configure how Nudge connects to your coding agent and uses your design system." }), _jsx(Dialog.Close, { ref: closeRef, className: "icon-button icon-button--quiet settings__close", "aria-label": "Close settings", "data-test": "settings-close", type: "button", children: _jsx(IconX, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) }), _jsxs("div", { className: "settings__body", children: [_jsxs("nav", { className: "settings__nav", "aria-label": "Settings sections", children: [_jsx("span", { className: "settings__nav-label", children: "Settings" }), _jsx("div", { className: "settings__nav-items", children: SETTINGS_SECTIONS.map((section) => (_jsxs("button", { className: `settings__nav-item${activeSection === section.id ? " settings__nav-item--active" : ""}`, "data-test": `settings-nav-${section.id}`, type: "button", "aria-current": activeSection === section.id ? "page" : undefined, onClick: () => setActiveSection(section.id), children: [section.icon, _jsx("span", { className: "settings__nav-item-label", children: section.label })] }, section.id))) }), _jsxs("button", { className: "settings__nav-item settings__reset-canvas", "data-test": "settings-reset-canvas", type: "button", onClick: resetCanvas, children: [_jsx(IconTrash, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), _jsx("span", { className: "settings__nav-item-label", children: "Reset canvas" })] })] }), _jsxs("main", { className: "settings__content", "data-test": "settings-content", children: [activeSection === "instructions" ? (_jsxs("section", { className: "settings__section", "data-test": "prompt-settings-dialog", "data-settings-section": "instructions", children: [_jsx("h2", { className: "settings__section-title", children: "Custom instructions" }), _jsx("p", { className: "settings__section-description", children: "These instructions are added to the end of every copied prompt." }), _jsx(PromptSettingsFields, { value: value, onChange: onValueChange, instructionsRef: instructionsRef })] })) : null, activeSection === "mcp" ? (_jsxs("section", { className: "settings__section", "data-test": "settings-section-mcp", children: [_jsx("h2", { className: "settings__section-title", children: activeDefinition.label }), _jsx("div", { "data-test": "mcp-connection-dialog", children: _jsx(McpConnectionContent, { projectId: projectId, origin: origin, snapshot: snapshot, onConnect: onConnect, onTakeOver: onTakeOver, onDisconnect: onDisconnect, onCheckAgain: onCheckAgain }) })] })) : null, activeSection === "tokens" ? (_jsxs("section", { className: "settings__section", "data-test": "settings-section-tokens", children: [_jsx("h2", { className: "settings__section-title", children: activeDefinition.label }), _jsx("p", { className: "settings__section-description", children: "Inspect and edit the design tokens available in this page." }), _jsx(TokensPanel, { rows: tokenRows })] })) : null] })] })] })] }) }));
56
69
  }
@@ -0,0 +1,11 @@
1
+ import type { ReactElement } from "react";
2
+ import { type AgentClientSnapshot } from "../agent/client.js";
3
+ import type { SettingsSection } from "../settings/SettingsDialog.js";
4
+ export interface AgentStatusButtonProps {
5
+ onOpenSettings: (section: SettingsSection) => void;
6
+ }
7
+ export declare function AgentStatusButton({ onOpenSettings }: AgentStatusButtonProps): ReactElement;
8
+ /** Renders the connection snapshot provided by the project client. */
9
+ export declare function AgentStatusButtonView({ agent, onOpenSettings }: AgentStatusButtonProps & {
10
+ agent: AgentClientSnapshot;
11
+ }): ReactElement;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { IconRobot } from "@tabler/icons-react";
3
+ import { useAgentClient } from "../agent/client.js";
4
+ import { getAgentConnectionStatus } from "../agent/connectionStatus.js";
5
+ import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
6
+ import { Button } from "../ui/Button.js";
7
+ import { Tooltip } from "../ui/Tooltip.js";
8
+ /** Renders a known MCP client logo, or a generic robot avatar. */
9
+ function ConnectedAgentAvatar({ name }) {
10
+ const harness = name?.toLowerCase().match(/(?:^|[^a-z])(codex|claude|cursor)(?:$|[^a-z])/)?.[1];
11
+ return (_jsx("span", { className: "agent-status-button__avatars", role: "img", "aria-label": harness === "codex" ? "Codex" : harness === "claude" ? "Claude" : harness === "cursor" ? "Cursor" : "Agent", children: harness === "codex" ? _jsx(CodexAvatar, {})
12
+ : harness === "claude" ? _jsx(ClaudeAvatar, {})
13
+ : harness === "cursor" ? _jsx(CursorAvatar, {})
14
+ : _jsx("span", { className: "agent-status-button__avatar", children: _jsx(IconRobot, { size: 24, color: "#000", "aria-hidden": "true" }) }) }));
15
+ }
16
+ function CodexAvatar() {
17
+ return (_jsx("span", { className: "agent-status-button__avatar", children: _jsx("svg", { viewBox: "0 0 24 24", width: "24", height: "24", fill: "none", children: _jsx("path", { fill: "#000", d: "M20.686 10.184a4.92 4.92 0 0 0-.435-4.092 5.1 5.1 0 0 0-2.35-2.093 5.17 5.17 0 0 0-3.15-.324A5.13 5.13 0 0 0 12.53 2.25a5.2 5.2 0 0 0-2.648-.14 5.15 5.15 0 0 0-2.364 1.185 5.05 5.05 0 0 0-1.447 2.19 5.1 5.1 0 0 0-1.953.85A5 5 0 0 0 2.693 7.9a4.98 4.98 0 0 0 .627 5.914 4.92 4.92 0 0 0 .432 4.092A5.1 5.1 0 0 0 6.104 20c.99.435 2.093.548 3.151.324.478.53 1.065.955 1.722 1.243a5.1 5.1 0 0 0 2.087.433 5.16 5.16 0 0 0 3.018-.968 5.05 5.05 0 0 0 1.858-2.537 5.1 5.1 0 0 0 1.953-.849 5 5 0 0 0 1.425-1.568 4.99 4.99 0 0 0-.632-5.894m-7.622 10.507a3.8 3.8 0 0 1-2.43-.867l.12-.067 4.037-2.299a.67.67 0 0 0 .332-.568v-5.614l1.706.974a.06.06 0 0 1 .032.043v4.653a3.73 3.73 0 0 1-1.114 2.646 3.83 3.83 0 0 1-2.683 1.099m-8.161-3.438a3.68 3.68 0 0 1-.452-2.511l.12.07 4.041 2.3a.66.66 0 0 0 .66 0l4.935-2.808v1.944a.07.07 0 0 1-.027.051l-4.09 2.326a3.85 3.85 0 0 1-2.88.373 3.8 3.8 0 0 1-2.307-1.745M3.84 8.58a3.78 3.78 0 0 1 1.998-1.644v4.73a.63.63 0 0 0 .328.564l4.912 2.796-1.707.973a.07.07 0 0 1-.06 0l-4.08-2.322a3.76 3.76 0 0 1-1.758-2.267 3.7 3.7 0 0 1 .367-2.83m14.02 3.213L12.934 8.97 14.635 8a.07.07 0 0 1 .06 0l4.08 2.326a3.77 3.77 0 0 1 1.467 1.508 3.7 3.7 0 0 1-.338 3.999 3.8 3.8 0 0 1-1.7 1.246v-4.73a.65.65 0 0 0-.343-.556m1.7-2.52-.12-.07-4.034-2.319a.66.66 0 0 0-.663 0L9.811 9.691V7.748a.05.05 0 0 1 .024-.051l4.08-2.323a3.84 3.84 0 0 1 4.073.174 3.76 3.76 0 0 0 1.332 1.63c.279.657.361 1.378.24 2.08zM8.88 12.72l-1.707-.97a.07.07 0 0 1-.032-.047v-4.64c.001-.712.207-1.409.595-2.01s.94-1.079 1.594-1.38a3.85 3.85 0 0 1 4.042.512l-.12.067-4.036 2.298a.67.67 0 0 0-.332.568zm.927-1.971 2.198-1.25 2.202 1.25v2.5l-2.194 1.25-2.202-1.25z" }) }) }));
18
+ }
19
+ function ClaudeAvatar() {
20
+ return (_jsx("span", { className: "agent-status-button__avatar", children: _jsx("svg", { viewBox: "0 0 24 24", width: "24", height: "24", fill: "none", children: _jsx("path", { fill: "#e8704e", d: "m5.924 15.296 3.934-2.206.066-.192-.066-.106h-.192l-.658-.04-2.248-.061-1.95-.081-1.888-.101-.476-.102L2 11.82l.046-.293.4-.268.572.05 1.266.086 1.898.132 1.377.08 2.041.213h.324l.046-.131-.112-.081-.086-.081-1.964-1.33L5.68 8.79l-1.114-.81-.602-.41-.304-.384-.132-.84.547-.602.734.05.188.051.744.572 1.59 1.23 2.076 1.527.304.253.121-.086.015-.06-.137-.228-1.129-2.04L7.377 4.94l-.536-.86-.142-.517a2.5 2.5 0 0 1-.086-.607l.623-.845L7.58 2l.83.111.35.304.516 1.179.835 1.857 1.297 2.524.38.749.202.693.076.213h.131v-.122l.107-1.421.197-1.746.193-2.246.065-.633.314-.759.623-.41.486.233.4.572-.055.37-.238 1.542-.466 2.419-.304 1.619h.177l.203-.203.82-1.087 1.377-1.72.608-.684.709-.753.455-.36h.861l.633.941-.284.972-.886 1.123-.734.951-1.053 1.417-.658 1.133.06.091.158-.015 2.38-.506 1.285-.233 1.535-.263.693.324.076.329-.273.673-1.64.405-1.925.384-2.866.678-.035.025.04.051 1.292.121.551.03h1.352l2.517.188.658.435.395.531-.066.405-1.012.516-1.368-.324-3.19-.759-1.093-.273h-.152v.091l.911.89 1.671 1.508 2.091 1.943.107.48-.269.38-.283-.04-1.838-1.381-.709-.623-1.605-1.35h-.106v.141l.37.542 1.954 2.934.1.9-.14.294-.507.177-.557-.101-1.144-1.604-1.18-1.806-.952-1.62-.117.067-.562 6.046-.263.308-.607.233-.507-.384-.268-.623.268-1.23.324-1.603.264-1.275.238-1.584.141-.526-.01-.035-.116.015-1.195 1.64-1.818 2.453-1.438 1.538-.344.137-.598-.309.056-.551.334-.491 1.99-2.53 1.2-1.568.775-.906-.005-.132h-.046l-5.286 3.43-.942.122-.405-.38.05-.622.193-.202 1.59-1.093z" }) }) }));
21
+ }
22
+ function CursorAvatar() {
23
+ return (_jsx("span", { className: "agent-status-button__avatar", children: _jsx("svg", { viewBox: "0 0 24 24", width: "24", height: "24", fill: "none", children: _jsx("path", { fill: "#000", d: "M11.576 2.11 3.359 6.731a.7.7 0 0 0-.262.256.7.7 0 0 0-.097.349v9.323c0 .25.138.48.359.604l8.214 4.625a.87.87 0 0 0 .853 0l8.215-4.625a.7.7 0 0 0 .262-.255.7.7 0 0 0-.097-.349V7.337a.7.7 0 0 0-.096-.35.7.7 0 0 0-.263-.255l-8.216-4.623a.88.88 0 0 0-.851 0m-7.56 5.173H19.87c.225 0 .367.24.254.43l-7.928 13.387c-.053.089-.196.053-.196-.05v-8.77a.48.48 0 0 0-.252-.425l-7.787-4.38c-.094-.053-.055-.192.052-.192" }) }) }));
24
+ }
25
+ export function AgentStatusButton({ onOpenSettings }) {
26
+ const runtimeConfig = useNudgeUiRuntimeConfig();
27
+ const agent = useAgentClient(runtimeConfig.projectId);
28
+ return _jsx(AgentStatusButtonView, { agent: agent, onOpenSettings: onOpenSettings });
29
+ }
30
+ /** Renders the connection snapshot provided by the project client. */
31
+ export function AgentStatusButtonView({ agent, onOpenSettings }) {
32
+ const status = getAgentConnectionStatus(agent);
33
+ const connected = agent.paired || agent.pairedElsewhere;
34
+ const listening = connected && agent.listenerActive;
35
+ return (_jsx(Tooltip, { content: connected ? status.label : "Agent & MCP settings", stableTrigger: true, children: _jsxs(Button, { variant: "quiet", className: "agent-status-button", "aria-label": connected ? `MCP · ${status.label}` : "Settings", "data-test": "settings-button", "data-mcp-connected": connected ? "true" : "false", "data-mcp-listening": listening ? "true" : "false", onClick: () => onOpenSettings("mcp"), children: [connected ? _jsx(ConnectedAgentAvatar, { name: agent.agentClientName }) : _jsxs("span", { className: "agent-status-button__avatars", children: [_jsx(CodexAvatar, {}), _jsx(ClaudeAvatar, {}), _jsx(CursorAvatar, {})] }), _jsx("span", { className: "agent-status-button__label", children: connected ? "MCP" : "Settings" }), connected ? (_jsx("span", { className: "agent-status-button__dot", "data-test": "agent-status-dot", "aria-hidden": "true" })) : null] }) }));
36
+ }
@@ -1,13 +1,15 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ 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
5
  import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
6
6
  import { readNudgeUiEditorTarget } from "../../transport/editor.js";
7
+ import { TooltipGroup } from "../ui/Tooltip.js";
8
+ import { CommentsOverlay } from "../comments/CommentsOverlay.js";
7
9
  export function AppShell() {
8
10
  const canvasEnabled = useNudgeUiRuntimeConfig().capabilities.canvas;
9
11
  const primaryUrl = typeof window === "undefined"
10
12
  ? null
11
13
  : readNudgeUiEditorTarget(window.location.href);
12
- return (_jsxs(_Fragment, { children: [canvasEnabled ? _jsx(CanvasWorkspace, { primaryUrl: primaryUrl }) : null, canvasEnabled ? _jsx(CanvasElementOverlay, {}) : null, _jsx(InspectorShell, {})] }));
14
+ return (_jsxs(TooltipGroup, { children: [canvasEnabled ? _jsx(CanvasWorkspace, { primaryUrl: primaryUrl }) : null, canvasEnabled ? _jsx(CanvasElementOverlay, {}) : null, canvasEnabled ? _jsx(CommentsOverlay, {}) : null, _jsx(InspectorShell, {})] }));
13
15
  }
@@ -1,15 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useMemo, useSyncExternalStore } from "react";
3
- import { isElementChange, useChanges, revertChange } from "../changes/changesLog.js";
3
+ import { IconArrowBackUp } from "@tabler/icons-react";
4
+ import { isComponentChange, isElementChange, isTextContentChange, isTokenChange, useChanges, revertChange, } from "../changes/changesLog.js";
4
5
  import { StaleChangeIndicator } from "../canvas/StaleChangeIndicator.js";
5
6
  import { Button } from "../ui/Button.js";
6
7
  import { Disclosure } from "../ui/Disclosure.js";
8
+ import { IconButton } from "../ui/IconButton.js";
9
+ import { Tooltip } from "../ui/Tooltip.js";
7
10
  import { presentChange } from "../changes/presentation.js";
8
11
  import { getStructuralChanges, getStructuralChangeDiagnostics, getStructuralDiagnosticRevision, revertStructuralChange, subscribeStructuralChanges, subscribeStructuralDiagnostics, } from "../projection/structuralProjection.js";
9
12
  import { getRenderedInstanceChangeDiagnostics, getRenderedInstanceDiagnosticRevision, subscribeRenderedInstanceDiagnostics, } from "../projection/renderedInstance.js";
10
13
  import { getTextContentChangeDiagnostics, getTextProjectionDiagnosticRevision, subscribeTextProjectionDiagnostics, } from "../projection/textProjection.js";
11
14
  import { SketchChanges } from "../sketch/SketchChanges.js";
12
15
  import { useSketchStore } from "../sketch/store.js";
16
+ import { useFrameComments } from "../comments/store.js";
17
+ import { countPromptChanges } from "../prompt/generatePrompt.js";
13
18
  function groupChanges(changes) {
14
19
  const map = new Map();
15
20
  for (const change of changes) {
@@ -20,7 +25,6 @@ function groupChanges(changes) {
20
25
  group = {
21
26
  key,
22
27
  label: presentation.groupLabel,
23
- file: presentation.file,
24
28
  changes: [],
25
29
  };
26
30
  map.set(key, group);
@@ -29,9 +33,6 @@ function groupChanges(changes) {
29
33
  }
30
34
  return Array.from(map.values());
31
35
  }
32
- function sourceFile(change) {
33
- return change.target.sourceSite.src.split(":").slice(0, -2).join(":") || change.target.sourceSite.src;
34
- }
35
36
  function structuralParentLabel(change, side) {
36
37
  const parent = side === "source" ? change.source.parent : change.destination.parent;
37
38
  const tag = side === "source" ? change.presentation.sourceParentTag : change.presentation.destinationParentTag;
@@ -60,8 +61,26 @@ function structuralReasonLabel(reason) {
60
61
  function structuralDiagnosticText(diagnostic) {
61
62
  return `${diagnostic.document}: ${diagnostic.status}${diagnostic.reason ? ` (${structuralReasonLabel(diagnostic.reason)})` : ""}`;
62
63
  }
64
+ function changeTargetLabel(change, presentation) {
65
+ if (isComponentChange(change))
66
+ return change.target.componentName;
67
+ if (isTextContentChange(change))
68
+ return change.target.sourceSite.cid;
69
+ if (isTokenChange(change))
70
+ return change.tokenName;
71
+ return isElementChange(change) ? change.cid : presentation.groupLabel;
72
+ }
73
+ function changeActionLabel(change, presentation) {
74
+ if (isTextContentChange(change))
75
+ return "Changed text";
76
+ return `Changed ${presentation.propertyLabel}`;
77
+ }
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" }) }) }));
80
+ }
63
81
  export function ChangesLog({ onClearSession }) {
64
82
  const changes = useChanges();
83
+ const comments = useFrameComments();
65
84
  const sketchStore = useSketchStore();
66
85
  const sketches = sketchStore.items;
67
86
  const structuralChanges = useSyncExternalStore(subscribeStructuralChanges, getStructuralChanges, getStructuralChanges);
@@ -71,26 +90,27 @@ export function ChangesLog({ onClearSession }) {
71
90
  useSyncExternalStore(subscribeRenderedInstanceDiagnostics, getRenderedInstanceDiagnosticRevision, getRenderedInstanceDiagnosticRevision);
72
91
  useSyncExternalStore(subscribeTextProjectionDiagnostics, getTextProjectionDiagnosticRevision, getTextProjectionDiagnosticRevision);
73
92
  const groups = useMemo(() => groupChanges(changes), [changes]);
74
- const total = changes.length + structuralChanges.length + sketches.length;
75
- if (total === 0 && sketchStore.error === null)
93
+ const total = countPromptChanges(changes, structuralChanges, sketches.length);
94
+ const hasLoggedChanges = total > 0 || sketchStore.error !== null;
95
+ if (!hasLoggedChanges && comments.length === 0)
76
96
  return null;
77
- return (_jsxs(_Fragment, { children: [_jsx(Disclosure, { className: "changes", "data-test": "changes-log", triggerDataTest: "changes-toggle", title: "Changes", badge: total > 0 ? _jsx("span", { className: "changes__count", children: total }) : null, panelClassName: "changes__content", children: groups.length === 0 && structuralChanges.length === 0 && sketches.length === 0 && sketchStore.error === null ? (_jsx("div", { className: "changes__empty", "data-test": "changes-empty", children: "No changes yet" })) : (_jsxs(_Fragment, { children: [groups.map((group) => (_jsxs("div", { className: "changes__group", "data-test": "changes-group", "data-cid": group.label, children: [_jsxs("div", { className: "changes__group-title", children: [_jsx("span", { children: group.label }), _jsx("span", { className: "changes__group-file", children: group.file })] }), group.changes.map((change, i) => {
78
- const presentation = presentChange(change);
79
- const instanceDiagnostics = isElementChange(change) && change.scope === "rendered-instance"
80
- && change.instanceOverride
81
- ? getRenderedInstanceChangeDiagnostics(change.instanceOverride.id)
82
- : [];
83
- const textDiagnostics = change.kind === "text-content"
84
- ? getTextContentChangeDiagnostics(change.id)
85
- : [];
86
- return (_jsxs("div", { className: "changes__row", "data-test": "change-row", "data-property": presentation.property, 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(Button, { size: "compact", className: "changes__revert", "data-test": "change-revert", "data-property": presentation.property, onClick: () => revertChange(change), children: "Revert" })] }, `${group.key}\u0000${presentation.property}\u0000${i}`));
87
- })] }, group.key))), structuralChanges.map((change) => (_jsxs("div", { className: "changes__group", "data-test": "dom-change", "data-cid": change.target.sourceSite.cid, children: [_jsxs("div", { className: "changes__group-title", children: [_jsx("span", { children: change.target.sourceSite.cid }), _jsx("span", { className: "changes__group-file", children: sourceFile(change) })] }), _jsxs("div", { className: "changes__row", "data-test": "dom-change-row", "data-action": change.kind, 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"
88
- ? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
89
- ? `${change.presentation.sourceParentTag} position ${change.presentation.fromIndex + 1}`
90
- : `${structuralParentLabel(change, "source")} position ${change.presentation.fromIndex + 1}`
91
- : "Visible" }), _jsx("span", { className: "changes__arrow", children: "\u2192" }), _jsx("span", { className: "changes__after", children: change.kind === "move"
92
- ? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
93
- ? `${change.presentation.destinationParentTag} position ${change.presentation.toIndex + 1}`
94
- : `${structuralParentLabel(change, "destination")} position ${change.presentation.toIndex + 1}`
95
- : "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(Button, { size: "compact", className: "changes__revert", "data-test": "dom-change-revert", onClick: () => revertStructuralChange(change.id), children: "Revert" })] })] }, change.id))), _jsx(SketchChanges, {})] })) }), 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] }));
97
+ return (_jsxs(_Fragment, { children: [hasLoggedChanges ? _jsx(Disclosure, { className: "changes", "data-test": "changes-log", triggerDataTest: "changes-toggle", title: "Changes", badge: total > 0 ? _jsx("span", { className: "changes__count", children: total }) : null, panelClassName: "changes__content", children: groups.length === 0 && structuralChanges.length === 0 && sketches.length === 0 && sketchStore.error === null ? (_jsx("div", { className: "changes__empty", "data-test": "changes-empty", children: "No changes yet" })) : (_jsxs(_Fragment, { children: [groups.map((group) => (_jsx("div", { className: "changes__group", "data-test": "changes-group", "data-cid": group.label, children: group.changes.map((change, i) => {
98
+ const presentation = presentChange(change);
99
+ const instanceDiagnostics = isElementChange(change) && change.scope === "rendered-instance"
100
+ && change.instanceOverride
101
+ ? getRenderedInstanceChangeDiagnostics(change.instanceOverride.id)
102
+ : [];
103
+ const textDiagnostics = change.kind === "text-content"
104
+ ? getTextContentChangeDiagnostics(change.id)
105
+ : [];
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"
108
+ ? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
109
+ ? `${change.presentation.sourceParentTag} position ${change.presentation.fromIndex + 1}`
110
+ : `${structuralParentLabel(change, "source")} position ${change.presentation.fromIndex + 1}`
111
+ : "Visible" }), _jsx("span", { className: "changes__arrow", children: "\u2192" }), _jsx("span", { className: "changes__after", children: change.kind === "move"
112
+ ? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
113
+ ? `${change.presentation.destinationParentTag} position ${change.presentation.toIndex + 1}`
114
+ : `${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] }));
96
116
  }
@@ -1,32 +1,52 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Menu } from "@base-ui/react/menu";
3
+ import { iterationId } from "../canvas/frameContent.js";
2
4
  import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
- import { IconCheck, IconCopy, IconPlugConnected, IconSend, } from "@tabler/icons-react";
5
+ import { IconArtboard, IconBoltFilled, IconCheck, IconChevronDown, IconCopy, IconPlugConnected, IconSend, } from "@tabler/icons-react";
4
6
  import { useChanges } from "../changes/changesLog.js";
5
- import { generatePrompt } from "../prompt/generatePrompt.js";
6
- import { copyToClipboard } from "../prompt/copyToClipboard.js";
7
+ import { countPromptChanges } from "../prompt/generatePrompt.js";
7
8
  import { loadCustomInstructions, saveCustomInstructions } from "../prompt/promptSettings.js";
8
9
  import { getAgentConnectionStatus } from "../agent/connectionStatus.js";
9
10
  import { SettingsDialog } from "../settings/SettingsDialog.js";
10
11
  import { Button } from "../ui/Button.js";
12
+ import { portalContainer } from "../ui/portalContainer.js";
11
13
  import { StatusCallout } from "../ui/StatusCallout.js";
12
14
  import { getStructuralChanges, subscribeStructuralChanges } from "../projection/structuralProjection.js";
13
15
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
14
- import { createPromptRevision, getAgentClient, useAgentClient, } from "../agent/client.js";
16
+ import { getAgentClient, useAgentClient } from "../agent/client.js";
15
17
  import { createAgentPresentationAdapter } from "../canvas/agentPresentation.js";
16
- import { discardAgentDispatch, recordAgentDispatch, verifyAndReconcileAgentDispatch, } from "../agent/verification.js";
17
- import { getClipboardHandoffRevision, getLastClipboardReconciledCount, recordClipboardHandoff, subscribeClipboardHandoff, } from "../prompt/clipboardHandoff.js";
18
- import { useSketches, markSketchesDispatching, markSketchesHandingOff, settleSketchDispatch } from "../sketch/store.js";
19
- import { createSketchAttachments, createSketchHandoffSnapshot, sketchMetadataForHandoff, } from "../sketch/handoff.js";
18
+ import { verifyAndReconcileAgentDispatch } from "../agent/verification.js";
19
+ import { getClipboardHandoffRevision, getLastClipboardReconciledCount, subscribeClipboardHandoff, } from "../prompt/clipboardHandoff.js";
20
+ import { useSketches, settleSketchDispatch } from "../sketch/store.js";
20
21
  import { SketchLayersPanel } from "../sketch/SketchLayersPanel.js";
21
22
  import { isSendPromptShortcut, SEND_PROMPT_HOTKEY_EVENT } from "./shortcuts.js";
23
+ import { getFocusedCardId, getSelectedCardId, useCanvasCards, useFocusedCardId, useSelectedCardId } from "../canvas/canvasStore.js";
24
+ import { getEditableCardId, sketchBelongsToCard, useDrafts } from "../drafts/store.js";
25
+ import { copyHandoff, deliverHandoff, prepareHandoff, prepareIterationImplementationPrompt } from "../workspace/handoff.js";
26
+ import { copyToClipboard } from "../prompt/copyToClipboard.js";
27
+ import { useFrameComments } from "../comments/store.js";
28
+ import { reconcileComments } from "../comments/verification.js";
29
+ import { getRegisteredFrames } from "../canvas/projection.js";
22
30
  export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, settingsSection: controlledSettingsSection, onOpenSettings, onSettingsOpenChange, } = {}) {
23
31
  const changes = useChanges();
32
+ const comments = useFrameComments();
33
+ useDrafts();
24
34
  const sketches = useSketches();
25
- const pendingSketches = sketches.filter((item) => item.status === "pending");
35
+ const selectedDraftCardId = useSelectedCardId();
36
+ const focusedDraftCardId = useFocusedCardId();
37
+ const activeDraftCardId = getEditableCardId(selectedDraftCardId, focusedDraftCardId);
38
+ const canvasCards = useCanvasCards();
39
+ const activeContent = canvasCards.find((card) => card.id === activeDraftCardId)?.content;
40
+ const activeArtifactId = iterationId(activeContent);
41
+ const visibleSketches = sketches.filter((item) => sketchBelongsToCard(item.document, activeDraftCardId));
42
+ const pendingSketches = visibleSketches.filter((item) => item.status === "pending");
26
43
  const structuralChanges = useSyncExternalStore(subscribeStructuralChanges, getStructuralChanges, getStructuralChanges);
27
44
  useSyncExternalStore(subscribeClipboardHandoff, getClipboardHandoffRevision, getClipboardHandoffRevision);
28
45
  const reconciledCount = getLastClipboardReconciledCount();
29
46
  const [copied, setCopied] = useState(false);
47
+ const [preparing, setPreparing] = useState(false);
48
+ const handoffBusy = useRef(false);
49
+ const [saveMessage, setSaveMessage] = useState(null);
30
50
  const [agentCompletionStatus, setAgentCompletionStatus] = useState(null);
31
51
  const [sketchFallback, setSketchFallback] = useState(null);
32
52
  const [localSettingsOpen, setLocalSettingsOpen] = useState(false);
@@ -39,8 +59,13 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
39
59
  const agent = useAgentClient(runtimeConfig.projectId, agentClient);
40
60
  const agentRef = useRef(agent);
41
61
  agentRef.current = agent;
42
- const hasChanges = changes.length + structuralChanges.length + pendingSketches.length > 0;
43
- const changeCount = changes.length + structuralChanges.length + sketches.length;
62
+ const hasChanges = Boolean(activeArtifactId) || countPromptChanges(changes, structuralChanges, pendingSketches.length) + comments.length > 0;
63
+ useEffect(() => {
64
+ if (!copied)
65
+ return;
66
+ const timer = window.setTimeout(() => setCopied(false), 1500);
67
+ return () => window.clearTimeout(timer);
68
+ }, [copied]);
44
69
  useEffect(() => {
45
70
  setCustomInstructions(loadCustomInstructions(runtimeConfig.projectId));
46
71
  }, [runtimeConfig.projectId]);
@@ -61,6 +86,13 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
61
86
  return;
62
87
  let active = true;
63
88
  void verifyAndReconcileAgentDispatch(revision)
89
+ .then(async (removed) => {
90
+ for (const [, iframe] of getRegisteredFrames()) {
91
+ if (iframe.contentDocument)
92
+ await reconcileComments(iframe.contentDocument);
93
+ }
94
+ return removed;
95
+ })
64
96
  .then((removed) => {
65
97
  if (!active)
66
98
  return;
@@ -95,95 +127,116 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
95
127
  && !connecting
96
128
  && !working;
97
129
  const canSend = agent.paired && agent.listenerActive && !working;
98
- const disabled = connecting || working || (!canConnect && !hasChanges);
130
+ const changeCount = countPromptChanges(changes, structuralChanges, canSend ? pendingSketches.length : 0) + comments.length;
131
+ const disabled = preparing || connecting || working || (!canConnect && !hasChanges);
99
132
  const agentStatus = getAgentConnectionStatus(agent);
100
133
  const statusAction = agentStatus.action;
101
- const label = copied
102
- ? "Copied!"
103
- : connecting
104
- ? "Connecting…"
105
- : working
106
- ? "Agent working…"
107
- : canConnect
108
- ? "Connect agent"
109
- : canSend
110
- ? "Send prompt"
111
- : "Copy prompt";
134
+ const label = preparing
135
+ ? "Saving iteration…"
136
+ : copied
137
+ ? "Copied!"
138
+ : connecting
139
+ ? "Connecting…"
140
+ : working
141
+ ? "Agent working…"
142
+ : canConnect && !activeArtifactId
143
+ ? "Connect agent"
144
+ : canSend && !activeArtifactId
145
+ ? "Send prompt"
146
+ : "Copy prompt";
112
147
  const icon = copied
113
148
  ? _jsx(IconCheck, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
114
- : canConnect || connecting
149
+ : !activeArtifactId && (canConnect || connecting)
115
150
  ? _jsx(IconPlugConnected, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
116
- : canSend || working
151
+ : !activeArtifactId && (canSend || working)
117
152
  ? _jsx(IconSend, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
118
153
  : _jsx(IconCopy, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" });
119
154
  async function onClick() {
120
- if (disabled)
121
- return;
122
- if (canConnect) {
123
- await agentClient.connect();
155
+ if (disabled || handoffBusy.current)
124
156
  return;
157
+ handoffBusy.current = true;
158
+ setPreparing(Boolean(activeArtifactId) && !canConnect);
159
+ try {
160
+ await handoff();
125
161
  }
126
- const hints = {
127
- framework: runtimeConfig.framework,
128
- stylingSystem: runtimeConfig.stylingSystem,
129
- };
130
- const sketchHandoff = createSketchHandoffSnapshot(pendingSketches);
131
- const sketchMetadata = sketchHandoff ? sketchMetadataForHandoff(sketchHandoff) : [];
132
- const text = generatePrompt(changes, hints, structuralChanges, customInstructions, sketchMetadata);
133
- setAgentCompletionStatus(null);
134
- setSketchFallback(null);
135
- if (canSend) {
136
- const revision = createPromptRevision(changes, structuralChanges, sketchMetadata);
137
- recordAgentDispatch(revision, changes, structuralChanges);
138
- const clientDispatchId = sketchHandoff?.localBatchId ?? `dispatch-${Date.now()}-${Math.random().toString(36).slice(2)}`;
139
- let attachments = undefined;
140
- try {
141
- if (sketchHandoff) {
142
- await markSketchesDispatching(sketchHandoff.entries.map((entry) => ({ id: entry.id, revision: entry.revision })), revision, sketchHandoff.localBatchId);
143
- attachments = await createSketchAttachments(sketchHandoff);
144
- }
145
- const response = await agentClient.dispatchPrompt(text, revision, {
146
- clientDispatchId,
147
- ...(attachments === undefined ? {} : { attachments }),
148
- });
149
- if (response) {
150
- if (sketchHandoff) {
151
- await markSketchesHandingOff(sketchHandoff.entries.map((entry) => ({ id: entry.id, revision: entry.revision })), revision, sketchHandoff.localBatchId, response.request.requestId);
152
- }
153
- return;
162
+ finally {
163
+ handoffBusy.current = false;
164
+ setPreparing(false);
165
+ }
166
+ }
167
+ async function copyIterationPrompt(target) {
168
+ if (!activeArtifactId || disabled || handoffBusy.current)
169
+ return;
170
+ handoffBusy.current = true;
171
+ setCopied(false);
172
+ setPreparing(true);
173
+ try {
174
+ const request = {
175
+ cardId: getEditableCardId(getSelectedCardId(), getFocusedCardId()),
176
+ hints: { framework: runtimeConfig.framework, stylingSystem: runtimeConfig.stylingSystem },
177
+ customInstructions,
178
+ pendingSketches,
179
+ };
180
+ if (target === "application") {
181
+ const prompt = await prepareIterationImplementationPrompt(request);
182
+ try {
183
+ await copyToClipboard(prompt);
154
184
  }
155
- if (sketchHandoff) {
156
- await settleSketchDispatch(revision, "failed", undefined, sketchHandoff.localBatchId);
157
- discardAgentDispatch(revision);
158
- const latestAgent = agentClient.getSnapshot();
159
- setSketchFallback({
160
- prompt: text,
161
- error: latestAgent.request?.error
162
- ?? latestAgent.error
163
- ?? "The agent did not accept the sketch attachments.",
164
- });
165
- return;
185
+ catch {
186
+ throw new Error("The iteration was saved, but the prompt could not be copied. Try again.");
166
187
  }
167
188
  }
168
- catch (error) {
169
- discardAgentDispatch(revision);
170
- if (sketchHandoff) {
171
- await settleSketchDispatch(revision, "failed", undefined, sketchHandoff.localBatchId).catch(() => undefined);
172
- setSketchFallback({
173
- prompt: text,
174
- error: error instanceof Error ? error.message : "The sketch could not be sent to the agent.",
175
- });
176
- return;
189
+ else {
190
+ const prepared = await prepareHandoff(request);
191
+ try {
192
+ await copyHandoff(prepared);
193
+ }
194
+ catch {
195
+ throw new Error("The iteration was saved, but the prompt could not be copied. Try again.");
177
196
  }
178
197
  }
179
- discardAgentDispatch(revision);
198
+ setSaveMessage(null);
199
+ setCopied(true);
200
+ }
201
+ catch (error) {
202
+ setSaveMessage(error instanceof Error ? error.message : "The prompt could not be copied. Try again.");
203
+ }
204
+ finally {
205
+ handoffBusy.current = false;
206
+ setPreparing(false);
180
207
  }
181
- await copyToClipboard(text);
182
- if (changes.length > 0 || structuralChanges.length > 0) {
183
- recordClipboardHandoff(changes, structuralChanges);
208
+ }
209
+ async function handoff() {
210
+ if (disabled)
211
+ return;
212
+ if (canConnect) {
213
+ await agentClient.connect();
214
+ return;
184
215
  }
216
+ let prepared;
217
+ try {
218
+ prepared = await prepareHandoff({
219
+ cardId: getEditableCardId(getSelectedCardId(), getFocusedCardId()),
220
+ hints: { framework: runtimeConfig.framework, stylingSystem: runtimeConfig.stylingSystem },
221
+ customInstructions,
222
+ pendingSketches,
223
+ });
224
+ setSaveMessage(null);
225
+ }
226
+ catch (error) {
227
+ setSaveMessage(error instanceof Error ? error.message : "The HTML iteration could not be saved.");
228
+ return;
229
+ }
230
+ setAgentCompletionStatus(null);
231
+ setSketchFallback(null);
232
+ const outcome = await deliverHandoff(prepared, runtimeConfig.projectId, canSend ? agentClient : null);
233
+ if (outcome.kind === "sketch-failed") {
234
+ setSketchFallback({ handoff: outcome.handoff, error: outcome.error });
235
+ return;
236
+ }
237
+ if (outcome.kind === "sent")
238
+ return;
185
239
  setCopied(true);
186
- window.setTimeout(() => setCopied(false), 1500);
187
240
  }
188
241
  useEffect(() => {
189
242
  function onKeydown(event) {
@@ -231,12 +284,8 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
231
284
  }
232
285
  openMcpConnection();
233
286
  }
234
- return (_jsxs("div", { className: "copy-prompt__stack", "data-test": "copy-prompt-control", children: [_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, "data-agent-listening": agentStatus.kind === "listening" ? "true" : undefined, "aria-busy": working || connecting ? "true" : undefined, title: agent.error, onClick: onClick, children: [icon, label, changeCount > 0 ? (_jsx("span", { className: "copy-prompt__change-count", "data-test": "copy-prompt-change-count", children: changeCount })) : null, agentStatus.kind === "listening" ? (_jsx("span", { className: "copy-prompt__agent-listening", "data-test": "agent-listening-indicator", role: "status", "aria-label": "Agent listening" })) : null] }), _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, agentCompletionStatus === "verified" ? (_jsx("p", { className: "copy-prompt__hint", "data-test": "agent-verified-hint", role: "status", children: "Agent changes verified." })) : 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: () => {
235
- void copyToClipboard(sketchFallback.prompt).then(() => {
236
- if (changes.length > 0 || structuralChanges.length > 0) {
237
- recordClipboardHandoff(changes, structuralChanges);
238
- }
239
- setSketchFallback(null);
240
- }).catch(() => undefined);
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);
241
290
  }, 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() })] }));
242
291
  }