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,15 +1,16 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { IconArtboard, IconBoltFilled, IconSparkles } from "@tabler/icons-react";
3
+ import { useCanvasPresentation } from "../canvas/viewStore.js";
4
+ import { Disclosure } from "../ui/Disclosure.js";
2
5
  import gridIllustration from "../ui/assets/grid.svg";
3
- function isMacPlatform() {
4
- if (typeof navigator === "undefined")
5
- return false;
6
- return /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent);
7
- }
6
+ import { getPlatformModifierKey, isMacPlatform } from "./shortcuts.js";
8
7
  export function EmptyState() {
8
+ const presentation = useCanvasPresentation();
9
+ const canvas = presentation === "canvas";
9
10
  const macPlatform = isMacPlatform();
10
- const modifierKey = macPlatform ? "⌘" : "Ctrl";
11
+ const modifierKey = getPlatformModifierKey();
11
12
  const modifierName = macPlatform ? "Command" : "Control";
12
13
  const optionKey = macPlatform ? "⌥" : "Alt";
13
14
  const optionName = macPlatform ? "Option" : "Alt";
14
- return (_jsxs("div", { className: "empty-state", "data-test": "empty-state", 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("div", { className: "empty-state__shortcuts", 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" }) })] })] }) })] }));
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
16
  }
@@ -1,14 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useState } from "react";
3
- import { IconLayoutSidebarRight, IconSettings } from "@tabler/icons-react";
3
+ import { IconLayoutSidebarRight } from "@tabler/icons-react";
4
4
  import { useInspectorOpen, toggleInspector, 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";
8
8
  import { resolveSelectionFromElement } from "../selection/resolveSelection.js";
9
+ import { AgentStatusButton } from "./AgentStatusButton.js";
9
10
  import { SpacingBox } from "../styleEditors/SpacingBox.js";
10
11
  import { Typography } from "../styleEditors/Typography.js";
11
12
  import { isTextRelevant } from "../styleEditors/textRelevance.js";
13
+ import { BackgroundEditor } from "../styleEditors/BackgroundEditor.js";
12
14
  import { ColorPicker } from "../styleEditors/ColorPicker.js";
13
15
  import { BorderEditor } from "../styleEditors/BorderEditor.js";
14
16
  import { AppearanceSection } from "../styleEditors/AppearanceSection.js";
@@ -25,12 +27,11 @@ import { StatusCallout } from "../ui/StatusCallout.js";
25
27
  import { IconButton } from "../ui/IconButton.js";
26
28
  import { UI_STYLES } from "../ui/styles.js";
27
29
  import { getActiveStyleState, setActiveStyleState } from "./styleState.js";
28
- import { isEditableEvent } from "./shortcuts.js";
30
+ import { getInspectorToggleShortcutLabel, isEditableEvent } from "./shortcuts.js";
29
31
  import { formatInspectorLabel } from "../ui/labels.js";
30
32
  import { FieldRow } from "../ui/FieldRow.js";
31
33
  import { Select } from "../ui/Select.js";
32
- import { clearRestoreCount, clearSession } from "../canvas/sessionStore.js";
33
- import { hasWriteLease } from "../canvas/workspaceLease.js";
34
+ import { clearSelectedFrameChanges } from "../canvas/sessionStore.js";
34
35
  import { useFocusedCardId, useSelectedCardId, } from "../canvas/canvasStore.js";
35
36
  import { getActiveCanvasFrame } from "../canvas/activeCanvasDocument.js";
36
37
  import { getElementWindow } from "../runtime/domRealm.js";
@@ -46,10 +47,8 @@ import { intersectTokenEntries } from "../inspection/selectionProperty.js";
46
47
  import { isNudgeUiDev } from "../runtime/devFlag.js";
47
48
  import { isDemoRuntime } from "../runtime/runtimeConfig.js";
48
49
  import { SketchWorkspace } from "../sketch/SketchWorkspace.js";
49
- import { cancelSketchInteraction, useSketchInteractionActive } from "../sketch/interaction.js";
50
- import { clearSketchClipboardHandoff } from "../sketch/handoff.js";
51
- import { clearSketchesForProject } from "../sketch/store.js";
52
- import { closeSketchNote } from "../sketch/sketchNote.js";
50
+ import { useSketchInteractionActive } from "../sketch/interaction.js";
51
+ import { Tooltip } from "../ui/Tooltip.js";
53
52
  function findFirstTokenRow(rows, properties) {
54
53
  for (const property of properties) {
55
54
  const row = findTokenRow(rows, property);
@@ -231,19 +230,12 @@ export function InspectorShell() {
231
230
  setSettingsOpen(true);
232
231
  }
233
232
  function clearInspectorSession() {
234
- clearSession();
235
- clearRestoreCount();
236
- if (!hasWriteLease())
237
- return;
238
- cancelSketchInteraction();
239
- closeSketchNote();
240
- clearSketchClipboardHandoff();
241
- void clearSketchesForProject(runtimeConfig.projectId).catch(() => undefined);
233
+ clearSelectedFrameChanges();
242
234
  }
243
- 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(IconButton, { variant: "quiet", label: "Collapse inspector", "data-test": "collapse-inspector", disabled: sketchActive, style: { marginLeft: "-8px" }, onClick: () => {
244
- cancelInlineTextEdit();
245
- setInspectorOpen(false);
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(IconButton, { variant: "quiet", label: "Settings", title: "Settings", "data-test": "settings-button", onClick: () => openSettings("instructions"), children: _jsx(IconSettings, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) })] })] }), _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: () => {
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: () => {
236
+ cancelInlineTextEdit();
237
+ 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: () => {
247
239
  const overrideId = relinkElement(selected.domElement);
248
240
  if (overrideId)
249
241
  discardChangesForInstanceOverride(overrideId);
@@ -260,5 +252,5 @@ export function InspectorShell() {
260
252
  return;
261
253
  setActiveStyleState(nextState);
262
254
  setStyleState(nextState);
263
- } }) })) : null] })) : null, !isMultiSelection ? _jsx(ComponentPropsSection, { selected: selected }) : null, selectionEditable ? (_jsx(AtRuleContextProvider, { rows: tokenRows, children: _jsxs("div", { className: "style-editors", "data-test": "style-editors", children: [_jsx(LayoutSection, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `layout-${styleState}`), _jsx(SpacingBox, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `spacing-${styleState}`), _jsx(AppearanceSection, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `appearance-${styleState}`), showText ? _jsxs(_Fragment, { children: [_jsx(Typography, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `type-${styleState}`), _jsx(ColorPicker, { element: selected, selection: styleSelection, property: "color", entries: tokenEntries, tokenRow: findTokenRow(tokenRows, "color") }, `color-${styleState}`)] }) : null, _jsx(ColorPicker, { element: selected, selection: styleSelection, property: "background-color", entries: tokenEntries, tokenRow: backgroundTokenRow }, `background-${styleState}`), _jsx(BorderEditor, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `border-${styleState}`), _jsx(BoxShadowEditor, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `box-shadow-${styleState}`)] }) })) : (_jsx(StatusCallout, { tone: "neutral", "data-test": "multi-selection-uneditable", children: "These repeated items are indistinguishable, so Nudge UI cannot safely target only part of the group. Select every repeated item to edit them together." }))] })) : (_jsx(EmptyState, {})), _jsx(ChangesLog, { onClearSession: clearInspectorSession })] })] }), sketchEnabled ? (_jsx(SketchWorkspace, { hostElement: sketchHost, projectId: runtimeConfig.projectId })) : null, !isOpen ? (_jsx("div", { className: "panel__restore", children: _jsx(IconButton, { variant: "quiet", label: "Show inspector", className: "panel__restore-button", "data-test": "show-inspector", onClick: () => setInspectorOpen(true), children: _jsx(IconLayoutSidebarRight, { size: 16, stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) }) })) : null] }));
255
+ } }) })) : null] })) : null, !isMultiSelection ? _jsx(ComponentPropsSection, { selected: selected }) : null, selectionEditable ? (_jsx(AtRuleContextProvider, { rows: tokenRows, children: _jsxs("div", { className: "style-editors", "data-test": "style-editors", children: [_jsx(LayoutSection, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `layout-${styleState}`), _jsx(SpacingBox, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `spacing-${styleState}`), _jsx(AppearanceSection, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `appearance-${styleState}`), showText ? _jsxs(_Fragment, { children: [_jsx(Typography, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `type-${styleState}`), _jsx(ColorPicker, { element: selected, selection: styleSelection, property: "color", entries: tokenEntries, tokenRow: findTokenRow(tokenRows, "color") }, `color-${styleState}`)] }) : null, _jsx(BackgroundEditor, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRow: backgroundTokenRow }, `background-${styleState}`), _jsx(BorderEditor, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `border-${styleState}`), _jsx(BoxShadowEditor, { element: selected, selection: styleSelection, entries: tokenEntries, tokenRows: tokenRows }, `box-shadow-${styleState}`)] }) })) : (_jsx(StatusCallout, { tone: "neutral", "data-test": "multi-selection-uneditable", children: "These repeated items are indistinguishable, so Nudge UI cannot safely target only part of the group. Select every repeated item to edit them together." }))] })) : (_jsx(EmptyState, {})), _jsx(ChangesLog, { onClearSession: clearInspectorSession })] })] }), sketchEnabled ? (_jsx(SketchWorkspace, { hostElement: sketchHost, projectId: runtimeConfig.projectId })) : null, !isOpen ? (_jsx("div", { className: "panel__restore", children: _jsx(Tooltip, { children: _jsx(IconButton, { variant: "quiet", label: "Show inspector", className: "panel__restore-button", "data-test": "show-inspector", onClick: () => setInspectorOpen(true), children: _jsx(IconLayoutSidebarRight, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) }) }) })) : null] }));
264
256
  }
@@ -1,12 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useSyncExternalStore } from "react";
3
3
  import { IconButton } from "../ui/IconButton.js";
4
+ import { Tooltip } from "../ui/Tooltip.js";
4
5
  import { PeekOriginalIcon } from "./PeekOriginalIcon.js";
5
- import { workspaceChangeStore } from "../changes/workspaceChanges.js";
6
+ import { draftChangeStore } from "../changes/draftChanges.js";
6
7
  import { setOriginalPreviewActive, useOriginalPreviewActive, } from "./originalPreview.js";
7
- function useWorkspaceChangeCount() {
8
- return useSyncExternalStore(workspaceChangeStore.subscribe, () => {
9
- const snapshot = workspaceChangeStore.getSnapshot();
8
+ function useDraftChangeCount() {
9
+ return useSyncExternalStore(draftChangeStore.subscribe, () => {
10
+ const snapshot = draftChangeStore.getSnapshot();
10
11
  return snapshot.changes.length + snapshot.structuralChanges.length;
11
12
  }, () => 0);
12
13
  }
@@ -17,7 +18,7 @@ function useWorkspaceChangeCount() {
17
18
  */
18
19
  export function PeekOriginalButton() {
19
20
  const active = useOriginalPreviewActive();
20
- const changeCount = useWorkspaceChangeCount();
21
+ const changeCount = useDraftChangeCount();
21
22
  const disabled = changeCount === 0;
22
23
  useEffect(() => {
23
24
  if (!active)
@@ -35,23 +36,23 @@ export function PeekOriginalButton() {
35
36
  useEffect(() => () => {
36
37
  setOriginalPreviewActive(false);
37
38
  }, []);
38
- return (_jsx(IconButton, { variant: "quiet", "data-test": "peek-original-button", label: "Hold to view original", title: disabled ? "No changes to preview" : "Hold to view original", "aria-pressed": active, "data-active": active ? "true" : "false", disabled: disabled, onPointerDown: (event) => {
39
- if (disabled)
40
- return;
41
- if (event.pointerType === "mouse" && event.button !== 0)
42
- return;
43
- event.currentTarget.setPointerCapture?.(event.pointerId);
44
- setOriginalPreviewActive(true);
45
- }, onPointerUp: () => setOriginalPreviewActive(false), onPointerCancel: () => setOriginalPreviewActive(false), onLostPointerCapture: () => setOriginalPreviewActive(false), onKeyDown: (event) => {
46
- if (disabled || event.repeat)
47
- return;
48
- if (event.key === " " || event.key === "Enter")
39
+ return (_jsx(Tooltip, { content: disabled ? "No changes to preview" : "Hold to view original", shortcut: disabled ? undefined : "\\", stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", "data-test": "peek-original-button", label: "Hold to view original", "aria-pressed": active, "data-active": active ? "true" : "false", disabled: disabled, onPointerDown: (event) => {
40
+ if (disabled)
41
+ return;
42
+ if (event.pointerType === "mouse" && event.button !== 0)
43
+ return;
44
+ event.currentTarget.setPointerCapture?.(event.pointerId);
49
45
  setOriginalPreviewActive(true);
50
- }, onKeyUp: (event) => {
51
- if (event.key === " " || event.key === "Enter")
52
- setOriginalPreviewActive(false);
53
- }, onBlur: () => setOriginalPreviewActive(false), onContextMenu: (event) => {
54
- if (active)
55
- event.preventDefault();
56
- }, children: _jsx(PeekOriginalIcon, {}) }));
46
+ }, onPointerUp: () => setOriginalPreviewActive(false), onPointerCancel: () => setOriginalPreviewActive(false), onLostPointerCapture: () => setOriginalPreviewActive(false), onKeyDown: (event) => {
47
+ if (disabled || event.repeat)
48
+ return;
49
+ if (event.key === " " || event.key === "Enter")
50
+ setOriginalPreviewActive(true);
51
+ }, onKeyUp: (event) => {
52
+ if (event.key === " " || event.key === "Enter")
53
+ setOriginalPreviewActive(false);
54
+ }, onBlur: () => setOriginalPreviewActive(false), onContextMenu: (event) => {
55
+ if (active)
56
+ event.preventDefault();
57
+ }, children: _jsx(PeekOriginalIcon, {}) }) }));
57
58
  }
@@ -1,6 +1,6 @@
1
1
  import { useSyncExternalStore } from "react";
2
- import { workspaceChangeStore } from "../changes/workspaceChanges.js";
3
- import { applyHostWorkspaceProjection, compileWorkspaceProjection, } from "../projection/workspaceProjection.js";
2
+ import { draftChangeStore } from "../changes/draftChanges.js";
3
+ import { applyHostDraftProjection, compileDraftProjection, } from "../projection/draftProjection.js";
4
4
  import { isEditorShellDocument } from "../runtime/editorShell.js";
5
5
  let previewingOriginal = false;
6
6
  const listeners = new Set();
@@ -29,8 +29,8 @@ function notifyOriginalPreviewListeners() {
29
29
  function applyEmptyHostProjection() {
30
30
  if (isEditorShellDocument())
31
31
  return;
32
- const { revision } = workspaceChangeStore.getSnapshot();
33
- applyHostWorkspaceProjection({
32
+ const { revision } = draftChangeStore.getSnapshot();
33
+ applyHostDraftProjection({
34
34
  sourceRevision: revision,
35
35
  managedStyles: { rules: [], css: "" },
36
36
  instanceOverrides: [],
@@ -42,7 +42,7 @@ function applyEmptyHostProjection() {
42
42
  function applyCanonicalHostProjection() {
43
43
  if (isEditorShellDocument())
44
44
  return;
45
- applyHostWorkspaceProjection(compileWorkspaceProjection(workspaceChangeStore.getSnapshot()));
45
+ applyHostDraftProjection(compileDraftProjection(draftChangeStore.getSnapshot()));
46
46
  }
47
47
  /**
48
48
  * Hold-to-view-original (NUD-30). While active the host document shows the
@@ -1,5 +1,8 @@
1
1
  export declare function isEditableTarget(target: EventTarget | null): boolean;
2
2
  export declare function isEditableEvent(event: KeyboardEvent): boolean;
3
+ export declare function isMacPlatform(): boolean;
4
+ export declare function getPlatformModifierKey(): "⌘" | "Ctrl";
5
+ export declare function getInspectorToggleShortcutLabel(): string;
3
6
  export declare const SEND_PROMPT_HOTKEY_EVENT = "nudge-ui-send-prompt-hotkey";
4
7
  export declare function isSendPromptShortcut(event: KeyboardEvent): boolean;
5
8
  export declare function isInspectorToggleShortcut(event: KeyboardEvent): boolean;
@@ -14,6 +14,17 @@ function deeplyFocusedElement() {
14
14
  export function isEditableEvent(event) {
15
15
  return event.composedPath().some(isEditableTarget) || isEditableTarget(deeplyFocusedElement());
16
16
  }
17
+ export function isMacPlatform() {
18
+ if (typeof navigator === "undefined")
19
+ return false;
20
+ return /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent);
21
+ }
22
+ export function getPlatformModifierKey() {
23
+ return isMacPlatform() ? "⌘" : "Ctrl";
24
+ }
25
+ export function getInspectorToggleShortcutLabel() {
26
+ return `${getPlatformModifierKey()}\\`;
27
+ }
17
28
  export const SEND_PROMPT_HOTKEY_EVENT = "nudge-ui-send-prompt-hotkey";
18
29
  export function isSendPromptShortcut(event) {
19
30
  if (event.repeat || event.altKey || isEditableEvent(event))
@@ -5,15 +5,19 @@ import { IconArrowsMove, IconCheck, IconMessageCirclePlus, IconPencil, IconRecta
5
5
  import { Button } from "../ui/Button.js";
6
6
  import { IconButton } from "../ui/IconButton.js";
7
7
  import { StatusCallout } from "../ui/StatusCallout.js";
8
+ import { Tooltip } from "../ui/Tooltip.js";
8
9
  import { renderAnnotatedPng } from "./raster.js";
9
10
  import { SketchSvgLayer } from "./freehand.js";
10
11
  import { findSketchElementAt, translateSketchAnnotation, translateSketchStroke, } from "./geometry.js";
11
12
  import { SKETCH_LIMITS, SKETCH_ANNOTATION_RADIUS, SKETCH_STROKE_COLOR, SKETCH_STROKE_OUTLINE, SKETCH_STROKE_WIDTH, } from "./model.js";
12
- function portalContainer() {
13
- return typeof document !== "undefined"
14
- ? document.getElementById("nudge-ui-root")?.shadowRoot ?? document.body
15
- : null;
16
- }
13
+ import { DRAG_CURSOR, SKETCH_CURSOR } from "../ui/customCursors.js";
14
+ import { portalContainer } from "../ui/portalContainer.js";
15
+ const SKETCH_TOOLS = [
16
+ { tool: "move", label: "Move", Icon: IconArrowsMove },
17
+ { tool: "pen", label: "Pen", Icon: IconPencil },
18
+ { tool: "rectangle", label: "Rectangle", Icon: IconRectangle },
19
+ { tool: "annotate", label: "Annotate", Icon: IconMessageCirclePlus },
20
+ ];
17
21
  function objectUrl(blob) {
18
22
  if (!blob || typeof URL === "undefined" || typeof URL.createObjectURL !== "function")
19
23
  return null;
@@ -47,8 +51,8 @@ function rectanglePoints(start, end) {
47
51
  }
48
52
  export function SketchEditor({ open, document: sketchDocument, captured, onCancel, onSave }) {
49
53
  const image = captured?.originalImage ?? sketchDocument?.originalImage ?? null;
50
- const imageWidth = captured?.imageWidth ?? sketchDocument?.imageWidth ?? 0;
51
- const imageHeight = captured?.imageHeight ?? sketchDocument?.imageHeight ?? 0;
54
+ const imageWidth = captured?.imageWidth ?? sketchDocument?.capture.imageWidth ?? 0;
55
+ const imageHeight = captured?.imageHeight ?? sketchDocument?.capture.imageHeight ?? 0;
52
56
  const capture = captured?.capture ?? sketchDocument?.capture ?? null;
53
57
  const [description, setDescription] = useState("");
54
58
  const [tool, setTool] = useState("pen");
@@ -361,14 +365,10 @@ export function SketchEditor({ open, document: sketchDocument, captured, onCance
361
365
  setSaving(true);
362
366
  setError(null);
363
367
  try {
364
- const rendered = await renderAnnotatedPng(image, imageWidth, imageHeight, strokes, committedAnnotations);
368
+ const rendered = await renderAnnotatedPng(image, imageWidth, imageHeight, strokes, committedAnnotations, description.trim());
365
369
  await onSave({
366
370
  ...(sketchDocument ? { id: sketchDocument.id } : {}),
367
- capture: {
368
- ...capture,
369
- imageWidth: rendered.width,
370
- imageHeight: rendered.height,
371
- },
371
+ capture,
372
372
  description: description.trim(),
373
373
  strokes,
374
374
  annotations: committedAnnotations,
@@ -398,5 +398,5 @@ export function SketchEditor({ open, document: sketchDocument, captured, onCance
398
398
  if (!open || !image || !capture)
399
399
  return null;
400
400
  return (_jsx(Dialog.Root, { open: open, modal: false, disablePointerDismissal: true, onOpenChange: (nextOpen) => { if (!nextOpen && !saving)
401
- onCancel(); }, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { className: "sketch__backdrop sketch__editor-backdrop", "data-test": "sketch-editor-backdrop" }), _jsxs(Dialog.Popup, { className: "sketch__popup", "data-test": "sketch-editor", initialFocus: false, onKeyDown: onEditorKeyDown, children: [_jsx(Dialog.Title, { className: "sketch__title sketch__sr-only", children: "Annotate viewport" }), _jsx(Dialog.Description, { className: "sketch__description sketch__sr-only", children: "Draw directly over the captured UI. Select Annotate to label an area with a numbered note." }), _jsx("div", { className: "sketch__canvas-frame", children: _jsxs("div", { className: "sketch__image-wrap", children: [imageUrl ? _jsx("img", { className: "sketch__image", src: imageUrl, alt: "Captured viewport", draggable: false }) : null, _jsx("svg", { ref: svgRef, className: `sketch__canvas sketch__canvas--${tool}`, width: imageWidth, height: imageHeight, viewBox: `0 0 ${imageWidth} ${imageHeight}`, preserveAspectRatio: "none", pointerEvents: "all", tabIndex: 0, "aria-label": "Sketch over the captured viewport", onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: finishPointer, onPointerCancel: finishPointer, children: _jsx(SketchSvgLayer, { strokes: draftStroke ? [...strokes, draftStroke] : strokes, annotations: annotations }) }), activeAnnotation ? (_jsxs("div", { className: "sketch__annotation-editor", role: "dialog", "aria-label": `Annotation ${activeAnnotation.number}`, style: annotationEditorStyle(activeAnnotation), children: [_jsxs("label", { className: "sketch__sr-only", htmlFor: "sketch-editor-annotation-description", children: ["Annotation ", activeAnnotation.number, " description"] }), _jsx("input", { ref: annotationInputRef, id: "sketch-editor-annotation-description", value: annotationDraft, maxLength: SKETCH_LIMITS.description, placeholder: "Describe this area\u2026", autoComplete: "off", "data-test": "sketch-editor-annotation-description", onChange: (event) => setAnnotationDraft(event.target.value), onKeyDown: onAnnotationKeyDown }), _jsx(IconButton, { label: "Cancel annotation", variant: "quiet", size: "compact", "data-test": "sketch-editor-annotation-cancel", onClick: cancelActiveAnnotation, children: _jsx(IconX, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }), _jsxs(Button, { variant: "primary", size: "compact", type: "button", "data-test": "sketch-editor-annotation-save", onClick: () => { commitActiveAnnotation(); }, children: [_jsx(IconCheck, { size: "var(--icon-size-small)", "aria-hidden": "true" }), "Add"] })] })) : null] }) }), _jsxs("div", { className: "sketch__floating-panel", children: [_jsxs("div", { className: "sketch__toolbar", role: "toolbar", "aria-label": "Sketch tools", children: [_jsx(IconButton, { label: "Move", title: "Move", variant: "quiet", "aria-pressed": tool === "move", "data-active": tool === "move", "data-test": "sketch-move", onClick: () => selectTool("move"), disabled: saving, children: _jsx(IconArrowsMove, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }), _jsx(IconButton, { label: "Pen", title: "Pen", variant: "quiet", "aria-pressed": tool === "pen", "data-active": tool === "pen", "data-test": "sketch-pen", onClick: () => selectTool("pen"), disabled: saving, children: _jsx(IconPencil, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }), _jsx(IconButton, { label: "Rectangle", title: "Rectangle", variant: "quiet", "aria-pressed": tool === "rectangle", "data-active": tool === "rectangle", "data-test": "sketch-rectangle", onClick: () => selectTool("rectangle"), disabled: saving, children: _jsx(IconRectangle, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }), _jsx(IconButton, { label: "Annotate", title: "Annotate", variant: "quiet", "aria-pressed": tool === "annotate", "data-active": tool === "annotate", "data-test": "sketch-annotate", onClick: () => selectTool("annotate"), disabled: saving, children: _jsx(IconMessageCirclePlus, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }), _jsx("span", { className: "sketch__toolbar-divider", "aria-hidden": "true" }), _jsx(Button, { variant: "quiet", type: "button", "data-test": "sketch-cancel", onClick: onCancel, disabled: saving, children: "Cancel" }), _jsxs(Button, { variant: "primary", type: "button", "data-test": "sketch-done", onClick: () => void save(), disabled: saving || (strokes.length === 0 && annotations.length === 0 && description.trim() === ""), children: [_jsx(IconCheck, { size: "var(--icon-size-small)", "aria-hidden": "true" }), saving ? "Saving…" : "Done"] })] }), error ? _jsx(StatusCallout, { tone: "danger", "data-test": "sketch-editor-error", children: error }) : null] })] })] }) }));
401
+ onCancel(); }, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { className: "sketch__backdrop sketch__editor-backdrop", "data-test": "sketch-editor-backdrop" }), _jsxs(Dialog.Popup, { className: "sketch__popup", "data-test": "sketch-editor", initialFocus: false, onKeyDown: onEditorKeyDown, children: [_jsx(Dialog.Title, { className: "sketch__title sketch__sr-only", children: "Annotate viewport" }), _jsx(Dialog.Description, { className: "sketch__description sketch__sr-only", children: "Draw directly over the captured UI. Select Annotate to label an area with a numbered note." }), _jsx("div", { className: "sketch__canvas-frame", children: _jsxs("div", { className: "sketch__image-wrap", children: [imageUrl ? _jsx("img", { className: "sketch__image", src: imageUrl, alt: "Captured viewport", draggable: false }) : null, _jsx("svg", { ref: svgRef, className: `sketch__canvas sketch__canvas--${tool}`, width: imageWidth, height: imageHeight, viewBox: `0 0 ${imageWidth} ${imageHeight}`, preserveAspectRatio: "none", pointerEvents: "all", style: { cursor: tool === "move" ? DRAG_CURSOR : SKETCH_CURSOR }, tabIndex: 0, "aria-label": "Sketch over the captured viewport", onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: finishPointer, onPointerCancel: finishPointer, children: _jsx(SketchSvgLayer, { strokes: draftStroke ? [...strokes, draftStroke] : strokes, annotations: annotations }) }), activeAnnotation ? (_jsxs("div", { className: "sketch__annotation-editor", role: "dialog", "aria-label": `Annotation ${activeAnnotation.number}`, style: annotationEditorStyle(activeAnnotation), children: [_jsxs("label", { className: "sketch__sr-only", htmlFor: "sketch-editor-annotation-description", children: ["Annotation ", activeAnnotation.number, " description"] }), _jsx("input", { ref: annotationInputRef, id: "sketch-editor-annotation-description", value: annotationDraft, maxLength: SKETCH_LIMITS.description, placeholder: "Describe this area\u2026", autoComplete: "off", "data-test": "sketch-editor-annotation-description", onChange: (event) => setAnnotationDraft(event.target.value), onKeyDown: onAnnotationKeyDown }), _jsx(Tooltip, { children: _jsx(IconButton, { label: "Cancel annotation", variant: "quiet", size: "compact", "data-test": "sketch-editor-annotation-cancel", onClick: cancelActiveAnnotation, children: _jsx(IconX, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }) }), _jsxs(Button, { variant: "primary", size: "compact", type: "button", "data-test": "sketch-editor-annotation-save", onClick: () => { commitActiveAnnotation(); }, children: [_jsx(IconCheck, { size: "var(--icon-size-small)", "aria-hidden": "true" }), "Add"] })] })) : null] }) }), _jsxs("div", { className: "sketch__floating-panel", children: [_jsxs("div", { className: "sketch__toolbar", role: "toolbar", "aria-label": "Sketch tools", children: [SKETCH_TOOLS.map(({ tool: option, label, Icon }) => (_jsx(Tooltip, { stableTrigger: true, children: _jsx(IconButton, { label: label, variant: "quiet", "aria-pressed": tool === option, "data-active": tool === option, "data-test": `sketch-${option}`, onClick: () => selectTool(option), disabled: saving, children: _jsx(Icon, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }) }, option))), _jsx("span", { className: "sketch__toolbar-divider", "aria-hidden": "true" }), _jsx(Button, { variant: "quiet", type: "button", "data-test": "sketch-cancel", onClick: onCancel, disabled: saving, children: "Cancel" }), _jsxs(Button, { variant: "primary", type: "button", "data-test": "sketch-done", onClick: () => void save(), disabled: saving || (strokes.length === 0 && annotations.length === 0 && description.trim() === ""), children: [_jsx(IconCheck, { size: "var(--icon-size-small)", "aria-hidden": "true" }), saving ? "Saving…" : "Done"] })] }), error ? _jsx(StatusCallout, { tone: "danger", "data-test": "sketch-editor-error", children: error }) : null] })] })] }) }));
402
402
  }
@@ -7,5 +7,5 @@ import { startSketchCapture } from "./SketchWorkspace.js";
7
7
  export function SketchEntryButton({ hostElement }) {
8
8
  const active = useSketchInteractionActive();
9
9
  const disabled = active || isInlineTextEditingActive();
10
- return (_jsxs(Button, { variant: "quiet", size: "compact", className: "panel__sketch-button", type: "button", "data-test": "sketch-entry", "aria-label": "Sketch viewport", title: "Sketch viewport", disabled: disabled || !hostElement, onClick: () => startSketchCapture(hostElement), children: [_jsx(IconPencil, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), _jsx("span", { className: "panel__sketch-label", children: "Sketch" })] }));
10
+ return (_jsxs(Button, { variant: "quiet", size: "compact", className: "panel__sketch-button", type: "button", "data-test": "sketch-entry", "aria-label": "Sketch viewport", disabled: disabled || !hostElement, onClick: () => startSketchCapture(hostElement), children: [_jsx(IconPencil, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), _jsx("span", { className: "panel__sketch-label", children: "Sketch" })] }));
11
11
  }
@@ -2,10 +2,12 @@ import type { ReactElement } from "react";
2
2
  import type { SketchDocument, SketchPoint } from "./model.js";
3
3
  interface SketchFrameOverlayProps {
4
4
  readonly iframe: HTMLIFrameElement | null;
5
+ readonly cardId?: string;
5
6
  readonly cardUrl: string;
7
+ readonly artifactId?: string;
6
8
  readonly ready: boolean;
7
9
  }
8
10
  export declare function sketchRouteKey(url: string): string | null;
9
11
  export declare function projectSketchPoint(point: SketchPoint, document: SketchDocument, frameWindow: Window, width: number, height: number): SketchPoint;
10
- export declare function SketchFrameOverlay({ iframe, cardUrl, ready }: SketchFrameOverlayProps): ReactElement | null;
12
+ export declare function SketchFrameOverlay({ cardId, iframe, cardUrl, artifactId, ready }: SketchFrameOverlayProps): ReactElement | null;
11
13
  export {};
@@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from "react";
3
3
  import { SketchSvgLayer } from "./freehand.js";
4
4
  import { useSketchStore } from "./store.js";
5
5
  import { getSketchScrollPosition } from "./capture.js";
6
+ import { sketchBelongsToCard, useDrafts } from "../drafts/store.js";
6
7
  export function sketchRouteKey(url) {
7
8
  try {
8
9
  const parsed = new URL(url);
@@ -27,16 +28,17 @@ function frameRoute(iframe, fallback) {
27
28
  return sketchRouteKey(fallback);
28
29
  }
29
30
  }
30
- function sameFrame(document, iframe, cardUrl) {
31
+ function sameFrame(document, iframe, cardUrl, artifactId) {
31
32
  const sketchRoute = sketchRouteKey(document.capture.url);
32
33
  if (!sketchRoute)
33
34
  return false;
34
- return sketchRoute === frameRoute(iframe, cardUrl);
35
+ return sketchRoute === frameRoute(iframe, cardUrl)
36
+ || (artifactId !== undefined && sketchRoute === sketchRouteKey(cardUrl));
35
37
  }
36
38
  export function projectSketchPoint(point, document, frameWindow, width, height) {
37
39
  const capture = document.capture;
38
- const imageWidth = Math.max(1, document.imageWidth);
39
- const imageHeight = Math.max(1, document.imageHeight);
40
+ const imageWidth = Math.max(1, document.capture.imageWidth);
41
+ const imageHeight = Math.max(1, document.capture.imageHeight);
40
42
  const viewportWidth = Math.max(1, frameWindow.innerWidth || width);
41
43
  const viewportHeight = Math.max(1, frameWindow.innerHeight || height);
42
44
  const scroll = getSketchScrollPosition(frameWindow);
@@ -53,7 +55,7 @@ export function projectSketchPoint(point, document, frameWindow, width, height)
53
55
  };
54
56
  }
55
57
  function visibleStroke(stroke, document, frameWindow, width, height) {
56
- const captureScale = document.capture.viewportWidth / Math.max(1, document.imageWidth);
58
+ const captureScale = document.capture.viewportWidth / Math.max(1, document.capture.imageWidth);
57
59
  const currentScale = width / Math.max(1, frameWindow.innerWidth || width);
58
60
  return {
59
61
  ...stroke,
@@ -61,12 +63,13 @@ function visibleStroke(stroke, document, frameWindow, width, height) {
61
63
  points: stroke.points.map((point) => projectSketchPoint(point, document, frameWindow, width, height)),
62
64
  };
63
65
  }
64
- export function SketchFrameOverlay({ iframe, cardUrl, ready }) {
66
+ export function SketchFrameOverlay({ cardId, iframe, cardUrl, artifactId, ready }) {
65
67
  const { items } = useSketchStore();
68
+ useDrafts();
66
69
  const [revision, setRevision] = useState(0);
67
70
  const documents = useMemo(() => ready && iframe
68
- ? items.map((item) => item.document).filter((document) => sameFrame(document, iframe, cardUrl))
69
- : [], [cardUrl, iframe, items, ready]);
71
+ ? items.map((item) => item.document).filter((document) => sketchBelongsToCard(document, cardId ?? null) && sameFrame(document, iframe, cardUrl, artifactId))
72
+ : [], [artifactId, cardId, cardUrl, iframe, items, ready]);
70
73
  useEffect(() => {
71
74
  if (!iframe || documents.length === 0)
72
75
  return;
@@ -6,6 +6,9 @@ import { IconButton } from "../ui/IconButton.js";
6
6
  import { copyImageToClipboard } from "./raster.js";
7
7
  import { closeSketchNote } from "./sketchNote.js";
8
8
  import { removeSketch, useSketchStore } from "./store.js";
9
+ import { useFocusedCardId, useSelectedCardId } from "../canvas/canvasStore.js";
10
+ import { sketchBelongsToCard, useDrafts } from "../drafts/store.js";
11
+ import { Tooltip } from "../ui/Tooltip.js";
9
12
  function SketchThumbnail({ item }) {
10
13
  const [url, setUrl] = useState(null);
11
14
  const hasMarks = item.document.strokes.length > 0 || (item.document.annotations?.length ?? 0) > 0;
@@ -22,7 +25,7 @@ function SketchThumbnail({ item }) {
22
25
  }, [hasMarks, item.document.annotatedImage]);
23
26
  return hasMarks && url ? (_jsx("img", { className: "sketch-layers__thumbnail", src: url, alt: "", "aria-hidden": "true" })) : (_jsx("span", { className: "sketch-layers__thumbnail sketch-layers__thumbnail--empty", "aria-hidden": "true" }));
24
27
  }
25
- function SketchLayer({ item, index }) {
28
+ function SketchLayer({ item, index, multiple, }) {
26
29
  const [copied, setCopied] = useState(false);
27
30
  const [error, setError] = useState(null);
28
31
  const busy = item.status === "dispatching" || item.status === "handing-off";
@@ -51,13 +54,18 @@ function SketchLayer({ item, index }) {
51
54
  setError(deleteError instanceof Error ? deleteError.message : "The sketch could not be deleted.");
52
55
  }
53
56
  }
54
- return (_jsxs("div", { className: "sketch-layers__item", "data-test": "sketch-layer", "data-sketch-id": item.document.id, children: [_jsxs(Button, { className: "sketch-layers__copy", variant: "secondary", type: "button", "data-test": "sketch-layer-copy-image", onClick: () => void copyImage(), disabled: busy, children: [_jsxs("span", { className: "sketch-layers__copy-label", children: [copied
57
+ return (_jsxs("div", { className: "sketch-layers__item", "data-test": "sketch-layer", "data-sketch-id": item.document.id, children: [_jsxs(Button, { className: "sketch-layers__copy", variant: "primary", type: "button", "data-test": "sketch-layer-copy-image", onClick: () => void copyImage(), disabled: busy, children: [_jsxs("span", { className: "sketch-layers__copy-label", children: [copied
55
58
  ? _jsx(IconCheck, { size: "var(--icon-size-small)", "aria-hidden": "true" })
56
- : _jsx(IconCopy, { size: "var(--icon-size-small)", "aria-hidden": "true" }), _jsx("span", { className: "sketch-layers__name", children: copied ? "Copied" : `Sketch ${index + 1}` })] }), _jsx(SketchThumbnail, { item: item })] }), _jsx(IconButton, { className: "sketch-layers__delete", variant: "quiet", size: "compact", label: "Delete sketch", title: "Delete sketch", "data-test": "sketch-layer-delete", onClick: () => void deleteLayer(), disabled: busy, children: _jsx(IconX, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }), error ? _jsx("span", { className: "sketch-layers__error", role: "status", children: error }) : null] }));
59
+ : _jsx(IconCopy, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), _jsx("span", { className: "sketch-layers__name", children: copied ? "Copied" : `Copy sketch note${multiple ? ` ${index + 1}` : ""}` })] }), _jsx(SketchThumbnail, { item: item })] }), _jsx(Tooltip, { children: _jsx(IconButton, { className: "sketch-layers__delete", variant: "quiet", size: "compact", label: "Delete sketch", "data-test": "sketch-layer-delete", onClick: () => void deleteLayer(), disabled: busy, children: _jsx(IconX, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }) }), error ? _jsx("span", { className: "sketch-layers__error", role: "status", children: error }) : null] }));
57
60
  }
58
61
  export function SketchLayersPanel() {
59
62
  const { items } = useSketchStore();
60
- if (items.length === 0)
63
+ useDrafts();
64
+ const selectedCardId = useSelectedCardId();
65
+ const focusedCardId = useFocusedCardId();
66
+ const cardId = selectedCardId ?? focusedCardId;
67
+ const visibleItems = items.filter((item) => sketchBelongsToCard(item.document, cardId));
68
+ if (visibleItems.length === 0)
61
69
  return null;
62
- return (_jsxs("section", { className: "sketch-layers", "data-test": "sketch-layers", "aria-label": "Sketch layers", children: [items.map((item, index) => (_jsx(SketchLayer, { item: item, index: index }, `${item.document.id}:${item.document.revision}`))), _jsx("p", { className: "sketch-layers__hint", "data-test": "sketch-copy-helper", children: "Copy prompt and sketch individually into agent" })] }));
70
+ return (_jsx("section", { className: "sketch-layers", "data-test": "sketch-layers", "aria-label": "Sketch layers", children: visibleItems.map((item, index) => (_jsx(SketchLayer, { item: item, index: index, multiple: visibleItems.length > 1 }, `${item.document.id}:${item.document.revision}`))) }));
63
71
  }
@@ -10,11 +10,10 @@ import { findSketchElementAt, translateSketchAnnotation, translateSketchStroke,
10
10
  import { SketchPromptPanel } from "./SketchPromptPanel.js";
11
11
  import { SketchSvgLayer } from "./freehand.js";
12
12
  import { useInspectorOpen } from "../shell/openStore.js";
13
- function portalContainer() {
14
- return typeof document !== "undefined"
15
- ? document.getElementById("nudge-ui-root")?.shadowRoot ?? document.body
16
- : null;
17
- }
13
+ import { DRAG_CURSOR, SKETCH_CURSOR } from "../ui/customCursors.js";
14
+ import { useTemporaryAppInteraction } from "../canvas/viewStore.js";
15
+ import { Tooltip } from "../ui/Tooltip.js";
16
+ import { portalContainer } from "../ui/portalContainer.js";
18
17
  function clamp(value, maximum) {
19
18
  return Math.max(0, Math.min(maximum, value));
20
19
  }
@@ -68,6 +67,7 @@ function annotationEditorStyle(annotation, viewport) {
68
67
  };
69
68
  }
70
69
  export function SketchOverlay({ open, hostElement, initialTool = "pen", onCancel, onDone }) {
70
+ const temporaryAppInteraction = useTemporaryAppInteraction();
71
71
  const inspectorOpen = useInspectorOpen();
72
72
  const [viewport, setViewport] = useState(() => getSketchViewport(hostElement));
73
73
  const [tool, setTool] = useState("pen");
@@ -411,10 +411,10 @@ export function SketchOverlay({ open, hostElement, initialTool = "pen", onCancel
411
411
  if (!open)
412
412
  return null;
413
413
  return (_jsx(Dialog.Root, { open: open, modal: false, disablePointerDismissal: true, onOpenChange: (nextOpen) => { if (!nextOpen && !submitting)
414
- onCancel(); }, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { className: "sketch__backdrop sketch__live-backdrop", "data-inspector-open": inspectorOpen ? "true" : "false", "data-test": "sketch-live-backdrop" }), _jsxs(Dialog.Popup, { className: "sketch__popup sketch__live-popup", "data-inspector-open": inspectorOpen ? "true" : "false", "data-test": "sketch-live-editor", onKeyDown: onEditorKeyDown, children: [_jsx(Dialog.Title, { className: "sketch__title sketch__sr-only", children: "Sketch viewport" }), _jsx(Dialog.Description, { className: "sketch__description sketch__sr-only", children: "Draw directly over the live interface, then add a short note for the sketch." }), _jsxs("div", { className: "sketch__canvas-frame sketch__live-frame", style: {
414
+ onCancel(); }, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { style: { visibility: temporaryAppInteraction ? "hidden" : undefined }, className: "sketch__backdrop sketch__live-backdrop", "data-inspector-open": inspectorOpen ? "true" : "false", "data-test": "sketch-live-backdrop" }), _jsxs(Dialog.Popup, { style: { visibility: temporaryAppInteraction ? "hidden" : undefined }, className: "sketch__popup sketch__live-popup", "data-inspector-open": inspectorOpen ? "true" : "false", "data-test": "sketch-live-editor", onKeyDown: onEditorKeyDown, children: [_jsx(Dialog.Title, { className: "sketch__title sketch__sr-only", children: "Sketch viewport" }), _jsx(Dialog.Description, { className: "sketch__description sketch__sr-only", children: "Draw directly over the live interface, then add a short note for the sketch." }), _jsxs("div", { className: "sketch__canvas-frame sketch__live-frame", style: {
415
415
  left: viewport.offsetX,
416
416
  top: viewport.offsetY,
417
417
  width: viewport.displayWidth,
418
418
  height: viewport.displayHeight,
419
- }, children: [_jsx("svg", { ref: svgRef, className: `sketch__canvas sketch__live-canvas sketch__canvas--${tool}`, width: viewport.width, height: viewport.height, viewBox: `0 0 ${viewport.width} ${viewport.height}`, preserveAspectRatio: "none", pointerEvents: "all", tabIndex: 0, "aria-label": "Sketch over the live interface", onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: finishPointer, onPointerCancel: finishPointer, children: _jsx(SketchSvgLayer, { strokes: draftStroke ? [...visibleStrokes, draftStroke] : visibleStrokes, annotations: visibleAnnotations }) }), activeAnnotation ? (_jsxs("div", { className: "sketch__annotation-editor", role: "dialog", "aria-label": `Annotation ${activeAnnotation.number}`, style: annotationEditorStyle(activeAnnotation, viewport), children: [_jsxs("label", { className: "sketch__sr-only", htmlFor: "sketch-annotation-description", children: ["Annotation ", activeAnnotation.number, " description"] }), _jsx("input", { ref: annotationInputRef, id: "sketch-annotation-description", value: annotationDraft, maxLength: SKETCH_LIMITS.description, placeholder: "Describe this area\u2026", autoComplete: "off", "data-test": "sketch-live-annotation-description", onChange: (event) => setAnnotationDraft(event.target.value), onKeyDown: onAnnotationKeyDown }), _jsx(IconButton, { label: "Cancel annotation", variant: "quiet", size: "compact", "data-test": "sketch-live-annotation-cancel", onClick: cancelActiveAnnotation, children: _jsx(IconX, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }), _jsxs(Button, { variant: "primary", size: "compact", type: "button", "data-test": "sketch-live-annotation-save", onClick: () => { commitActiveAnnotation(); }, children: [_jsx(IconCheck, { size: "var(--icon-size-small)", "aria-hidden": "true" }), "Add"] })] })) : null] }), _jsx(SketchPromptPanel, { dataTest: "sketch-live-prompt", className: "sketch__live-note-panel", description: description, error: error, saving: submitting, autoFocus: true, onDescriptionChange: setDescription, onDone: done, onCancel: onCancel })] })] }) }));
419
+ }, children: [_jsx("svg", { ref: svgRef, className: `sketch__canvas sketch__live-canvas sketch__canvas--${tool}`, width: viewport.width, height: viewport.height, viewBox: `0 0 ${viewport.width} ${viewport.height}`, preserveAspectRatio: "none", pointerEvents: "all", style: { cursor: tool === "move" ? DRAG_CURSOR : SKETCH_CURSOR }, tabIndex: 0, "aria-label": "Sketch over the live interface", onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: finishPointer, onPointerCancel: finishPointer, children: _jsx(SketchSvgLayer, { strokes: draftStroke ? [...visibleStrokes, draftStroke] : visibleStrokes, annotations: visibleAnnotations }) }), activeAnnotation ? (_jsxs("div", { className: "sketch__annotation-editor", role: "dialog", "aria-label": `Annotation ${activeAnnotation.number}`, style: annotationEditorStyle(activeAnnotation, viewport), children: [_jsxs("label", { className: "sketch__sr-only", htmlFor: "sketch-annotation-description", children: ["Annotation ", activeAnnotation.number, " description"] }), _jsx("input", { ref: annotationInputRef, id: "sketch-annotation-description", value: annotationDraft, maxLength: SKETCH_LIMITS.description, placeholder: "Describe this area\u2026", autoComplete: "off", "data-test": "sketch-live-annotation-description", onChange: (event) => setAnnotationDraft(event.target.value), onKeyDown: onAnnotationKeyDown }), _jsx(Tooltip, { children: _jsx(IconButton, { label: "Cancel annotation", variant: "quiet", size: "compact", "data-test": "sketch-live-annotation-cancel", onClick: cancelActiveAnnotation, children: _jsx(IconX, { size: "var(--icon-size-small)", "aria-hidden": "true" }) }) }), _jsxs(Button, { variant: "primary", size: "compact", type: "button", "data-test": "sketch-live-annotation-save", onClick: () => { commitActiveAnnotation(); }, children: [_jsx(IconCheck, { size: "var(--icon-size-small)", "aria-hidden": "true" }), "Add"] })] })) : null] }), _jsx(SketchPromptPanel, { dataTest: "sketch-live-prompt", className: "sketch__live-note-panel", description: description, error: error, saving: submitting, autoFocus: true, onDescriptionChange: setDescription, onDone: done, onCancel: onCancel })] })] }) }));
420
420
  }
@@ -1,4 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { assignSketchToDraft } from "../drafts/store.js";
3
+ import { getActiveDraftChanges } from "../changes/draftChanges.js";
2
4
  import { useEffect, useState } from "react";
3
5
  import { renderAnnotatedPng } from "./raster.js";
4
6
  import { closeSketchEditor, completeSketchCapture, beginSketchCapture, cancelSketchInteraction, openSketchEditor, useSketchInteraction } from "./interaction.js";
@@ -76,22 +78,22 @@ export function SketchWorkspace({ hostElement, projectId }) {
76
78
  ? store.items.find((item) => item.document.id === note.sketchId)?.document ?? null
77
79
  : null;
78
80
  async function save(input) {
79
- await saveSketch(input);
81
+ const owner = interaction.draftId ?? getActiveDraftChanges().draftId;
82
+ const saved = await saveSketch(input);
83
+ if (!input.id)
84
+ assignSketchToDraft(owner, saved.id);
80
85
  closeSketchEditor();
81
86
  }
82
87
  async function saveLive(draft) {
83
88
  if (!hostElement)
84
89
  throw new Error("The inspector is no longer available.");
90
+ const owner = interaction.draftId ?? getActiveDraftChanges().draftId;
85
91
  const captured = await completeSketchCapture(hostElement);
86
92
  const strokes = projectLiveStrokes(draft, captured);
87
93
  const annotations = projectLiveAnnotations(draft, captured);
88
- const rendered = await renderAnnotatedPng(captured.originalImage, captured.imageWidth, captured.imageHeight, strokes, annotations);
89
- await save({
90
- capture: {
91
- ...captured.capture,
92
- imageWidth: rendered.width,
93
- imageHeight: rendered.height,
94
- },
94
+ const rendered = await renderAnnotatedPng(captured.originalImage, captured.imageWidth, captured.imageHeight, strokes, annotations, draft.description);
95
+ const saved = await saveSketch({
96
+ capture: captured.capture,
95
97
  description: draft.description,
96
98
  strokes,
97
99
  annotations,
@@ -100,20 +102,24 @@ export function SketchWorkspace({ hostElement, projectId }) {
100
102
  originalImage: captured.originalImage,
101
103
  annotatedImage: rendered.blob,
102
104
  });
105
+ assignSketchToDraft(owner, saved.id);
106
+ closeSketchEditor();
103
107
  }
104
108
  async function saveNote(description) {
105
109
  if (!noteDocument)
106
110
  return;
111
+ const nextDescription = description.trim();
112
+ const rendered = await renderAnnotatedPng(noteDocument.originalImage, noteDocument.capture.imageWidth, noteDocument.capture.imageHeight, noteDocument.strokes, noteDocument.annotations ?? [], nextDescription);
107
113
  await saveSketch({
108
114
  id: noteDocument.id,
109
115
  capture: noteDocument.capture,
110
- description: description.trim(),
116
+ description: nextDescription,
111
117
  strokes: noteDocument.strokes,
112
118
  annotations: noteDocument.annotations ?? [],
113
- imageWidth: noteDocument.imageWidth,
114
- imageHeight: noteDocument.imageHeight,
119
+ imageWidth: rendered.width,
120
+ imageHeight: rendered.height,
115
121
  originalImage: noteDocument.originalImage,
116
- annotatedImage: noteDocument.annotatedImage,
122
+ annotatedImage: rendered.blob,
117
123
  });
118
124
  closeSketchNote();
119
125
  }