nudge-ui 0.2.0 → 0.2.2

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 (231) hide show
  1. package/bin/nudge-ui.mjs +3 -1
  2. package/dist/cli/agentCommand.d.ts +8 -0
  3. package/dist/cli/agentCommand.js +59 -0
  4. package/dist/client.mjs +878 -11
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
  6. package/dist/hosts/astro/clientTransport.js +63 -11
  7. package/dist/hosts/astro/integration.d.ts +2 -2
  8. package/dist/hosts/astro/integration.js +4 -3
  9. package/dist/hosts/environment.d.ts +15 -0
  10. package/dist/hosts/environment.js +16 -0
  11. package/dist/hosts/next/sidecar.d.ts +4 -0
  12. package/dist/hosts/next/sidecar.js +56 -7
  13. package/dist/hosts/next/wrapper.d.ts +2 -0
  14. package/dist/hosts/next/wrapper.js +32 -3
  15. package/dist/hosts/projectBridge.d.ts +21 -0
  16. package/dist/hosts/projectBridge.js +98 -0
  17. package/dist/hosts/static/cli.d.ts +1 -1
  18. package/dist/hosts/static/cli.js +6 -1
  19. package/dist/hosts/static/manifest.d.ts +1 -1
  20. package/dist/hosts/static/manifest.js +3 -1
  21. package/dist/hosts/static/server.js +45 -3
  22. package/dist/hosts/vite/vite.d.ts +6 -1
  23. package/dist/hosts/vite/vite.js +82 -15
  24. package/dist/inspector/agent/McpConnectionDialog.d.ts +5 -4
  25. package/dist/inspector/agent/McpConnectionDialog.js +20 -37
  26. package/dist/inspector/agent/client.d.ts +16 -3
  27. package/dist/inspector/agent/client.js +114 -11
  28. package/dist/inspector/agent/connectionStatus.d.ts +3 -3
  29. package/dist/inspector/agent/connectionStatus.js +11 -4
  30. package/dist/inspector/agent/httpTransport.d.ts +8 -0
  31. package/dist/inspector/agent/httpTransport.js +78 -18
  32. package/dist/inspector/agent/protocol.d.ts +11 -3
  33. package/dist/inspector/agent/protocol.js +1 -1
  34. package/dist/inspector/agent/verification.d.ts +1 -1
  35. package/dist/inspector/agent/verification.js +69 -12
  36. package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
  37. package/dist/inspector/canvas/CanvasCard.js +181 -91
  38. package/dist/inspector/canvas/CanvasElementOverlay.js +288 -52
  39. package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
  40. package/dist/inspector/canvas/CanvasToolbar.js +24 -0
  41. package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
  42. package/dist/inspector/canvas/CanvasWorkspace.js +410 -72
  43. package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
  44. package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
  45. package/dist/inspector/canvas/canvasResize.d.ts +30 -0
  46. package/dist/inspector/canvas/canvasResize.js +52 -0
  47. package/dist/inspector/canvas/canvasStore.d.ts +30 -6
  48. package/dist/inspector/canvas/canvasStore.js +128 -61
  49. package/dist/inspector/canvas/frameProtocol.d.ts +82 -5
  50. package/dist/inspector/canvas/frameProtocol.js +63 -2
  51. package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -0
  52. package/dist/inspector/canvas/keyboardShortcuts.js +16 -0
  53. package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
  54. package/dist/inspector/canvas/linkEligibility.js +0 -29
  55. package/dist/inspector/canvas/projection.d.ts +7 -0
  56. package/dist/inspector/canvas/projection.js +118 -25
  57. package/dist/inspector/canvas/rendererBootstrap.js +42 -29
  58. package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
  59. package/dist/inspector/canvas/rendererElementSelector.js +385 -58
  60. package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
  61. package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
  62. package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
  63. package/dist/inspector/canvas/sessionStore.d.ts +1 -0
  64. package/dist/inspector/canvas/sessionStore.js +12 -4
  65. package/dist/inspector/canvas/staleChangeDetector.js +16 -14
  66. package/dist/inspector/changes/changesLog.js +31 -1
  67. package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
  68. package/dist/inspector/changes/managedStyleProjection.js +3 -3
  69. package/dist/inspector/changes/structuralTypes.d.ts +4 -0
  70. package/dist/inspector/changes/workspaceChanges.d.ts +4 -0
  71. package/dist/inspector/changes/workspaceChanges.js +29 -3
  72. package/dist/inspector/clientManifest.d.ts +7 -0
  73. package/dist/inspector/clientManifest.js +12 -0
  74. package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
  75. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
  76. package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
  77. package/dist/inspector/index.d.ts +2 -0
  78. package/dist/inspector/index.js +80 -32
  79. package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
  80. package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
  81. package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
  82. package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
  83. package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
  84. package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
  85. package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
  86. package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
  87. package/dist/inspector/inline-text/textEditChange.js +165 -0
  88. package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
  89. package/dist/inspector/inspection/selectionProperty.js +1 -0
  90. package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
  91. package/dist/inspector/overlay/DomNavigator.js +40 -0
  92. package/dist/inspector/overlay/InspectorOverlay.js +6 -2
  93. package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
  94. package/dist/inspector/overlay/clickPolicy.js +34 -3
  95. package/dist/inspector/overlay/elementSelector.d.ts +1 -1
  96. package/dist/inspector/overlay/elementSelector.js +18 -7
  97. package/dist/inspector/overlay/selectedGeometry.js +21 -2
  98. package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
  99. package/dist/inspector/overlay/spacingGestures.js +351 -0
  100. package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
  101. package/dist/inspector/overlay/structuralGestures.js +12 -10
  102. package/dist/inspector/projection/managedStylesheet.js +4 -1
  103. package/dist/inspector/projection/renderedInstance.d.ts +2 -0
  104. package/dist/inspector/projection/renderedInstance.js +5 -1
  105. package/dist/inspector/projection/structuralProjection.d.ts +2 -0
  106. package/dist/inspector/projection/structuralProjection.js +85 -1
  107. package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
  108. package/dist/inspector/projection/textProjection.d.ts +2 -0
  109. package/dist/inspector/projection/textProjection.js +10 -4
  110. package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
  111. package/dist/inspector/prompt/clipboardHandoff.js +3 -3
  112. package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
  113. package/dist/inspector/prompt/generatePrompt.js +19 -2
  114. package/dist/inspector/runtime/editorShell.d.ts +2 -0
  115. package/dist/inspector/runtime/editorShell.js +4 -0
  116. package/dist/inspector/runtime/runtimeConfig.d.ts +5 -1
  117. package/dist/inspector/runtime/runtimeConfig.js +4 -0
  118. package/dist/inspector/selection/hierarchy.d.ts +6 -0
  119. package/dist/inspector/selection/hierarchy.js +8 -0
  120. package/dist/inspector/selection/selectionStore.js +2 -0
  121. package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
  122. package/dist/inspector/settings/SettingsDialog.js +12 -2
  123. package/dist/inspector/shell/AppShell.js +5 -1
  124. package/dist/inspector/shell/ChangesLog.js +26 -22
  125. package/dist/inspector/shell/CopyPromptButton.js +102 -19
  126. package/dist/inspector/shell/EmptyState.js +1 -1
  127. package/dist/inspector/shell/InspectorShell.d.ts +0 -1
  128. package/dist/inspector/shell/InspectorShell.js +38 -42
  129. package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
  130. package/dist/inspector/shell/PeekOriginalButton.js +57 -0
  131. package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
  132. package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
  133. package/dist/inspector/shell/originalPreview.d.ts +9 -0
  134. package/dist/inspector/shell/originalPreview.js +61 -0
  135. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  136. package/dist/inspector/shell/shortcuts.js +15 -0
  137. package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
  138. package/dist/inspector/sketch/SketchChanges.js +72 -0
  139. package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
  140. package/dist/inspector/sketch/SketchEditor.js +402 -0
  141. package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
  142. package/dist/inspector/sketch/SketchEntryButton.js +11 -0
  143. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
  144. package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
  145. package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
  146. package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
  147. package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
  148. package/dist/inspector/sketch/SketchOverlay.js +420 -0
  149. package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
  150. package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
  151. package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
  152. package/dist/inspector/sketch/SketchWorkspace.js +152 -0
  153. package/dist/inspector/sketch/capture.d.ts +55 -0
  154. package/dist/inspector/sketch/capture.js +350 -0
  155. package/dist/inspector/sketch/freehand.d.ts +13 -0
  156. package/dist/inspector/sketch/freehand.js +64 -0
  157. package/dist/inspector/sketch/geometry.d.ts +17 -0
  158. package/dist/inspector/sketch/geometry.js +98 -0
  159. package/dist/inspector/sketch/handoff.d.ts +21 -0
  160. package/dist/inspector/sketch/handoff.js +72 -0
  161. package/dist/inspector/sketch/interaction.d.ts +21 -0
  162. package/dist/inspector/sketch/interaction.js +98 -0
  163. package/dist/inspector/sketch/model.d.ts +104 -0
  164. package/dist/inspector/sketch/model.js +282 -0
  165. package/dist/inspector/sketch/persistence.d.ts +21 -0
  166. package/dist/inspector/sketch/persistence.js +205 -0
  167. package/dist/inspector/sketch/raster.d.ts +22 -0
  168. package/dist/inspector/sketch/raster.js +203 -0
  169. package/dist/inspector/sketch/sketchNote.d.ts +10 -0
  170. package/dist/inspector/sketch/sketchNote.js +30 -0
  171. package/dist/inspector/sketch/store.d.ts +47 -0
  172. package/dist/inspector/sketch/store.js +434 -0
  173. package/dist/inspector/styleEditors/BorderEditor.js +15 -19
  174. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
  175. package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
  176. package/dist/inspector/styleEditors/ColorPicker.js +11 -36
  177. package/dist/inspector/styleEditors/GapField.d.ts +3 -2
  178. package/dist/inspector/styleEditors/GapField.js +2 -2
  179. package/dist/inspector/styleEditors/GridSection.js +1 -1
  180. package/dist/inspector/styleEditors/LayoutSection.js +11 -8
  181. package/dist/inspector/styleEditors/SpacingBox.js +31 -33
  182. package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
  183. package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
  184. package/dist/inspector/styleEditors/layoutValue.js +3 -0
  185. package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
  186. package/dist/inspector/styleEditors/nudgeValue.js +93 -5
  187. package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
  188. package/dist/inspector/styleEditors/stylePresence.js +22 -0
  189. package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
  190. package/dist/inspector/styleEditors/textRelevance.js +35 -0
  191. package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
  192. package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
  193. package/dist/inspector/tokens/TokenField.js +130 -7
  194. package/dist/inspector/tokens/TokensPanel.js +2 -1
  195. package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
  196. package/dist/inspector/tokens/dragPointerLock.js +70 -0
  197. package/dist/inspector/tokens/editActions.d.ts +2 -0
  198. package/dist/inspector/tokens/editActions.js +2 -2
  199. package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
  200. package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
  201. package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
  202. package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
  203. package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
  204. package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
  205. package/dist/inspector/tokens/resolution.d.ts +3 -26
  206. package/dist/inspector/tokens/resolution.js +44 -735
  207. package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
  208. package/dist/inspector/tokens/tokenAvailability.js +204 -0
  209. package/dist/inspector/ui/Button.d.ts +1 -1
  210. package/dist/inspector/ui/Disclosure.d.ts +22 -0
  211. package/dist/inspector/ui/Disclosure.js +13 -0
  212. package/dist/inspector/ui/SideValuesField.js +2 -2
  213. package/dist/inspector/ui/StatusCallout.d.ts +4 -1
  214. package/dist/inspector/ui/StatusCallout.js +2 -2
  215. package/dist/inspector/ui/dragCursor.d.ts +8 -0
  216. package/dist/inspector/ui/dragCursor.js +21 -0
  217. package/dist/inspector/ui/styles.js +21 -17
  218. package/dist/internal/agent-protocol.d.ts +65 -1
  219. package/dist/internal/agent-protocol.js +183 -3
  220. package/dist/nudge-ui.mjs +276 -58
  221. package/dist/transport/editor.d.ts +47 -0
  222. package/dist/transport/editor.js +163 -0
  223. package/dist/transport/index.d.ts +1 -0
  224. package/dist/transport/index.js +1 -0
  225. package/dist/transport/routes.d.ts +10 -0
  226. package/dist/transport/routes.js +10 -0
  227. package/package.json +3 -1
  228. package/dist/inspector/shell/DomNavigation.d.ts +0 -8
  229. package/dist/inspector/shell/DomNavigation.js +0 -30
  230. package/dist/inspector/shell/panelLayout.d.ts +0 -2
  231. package/dist/inspector/shell/panelLayout.js +0 -29
@@ -137,7 +137,7 @@ function pruneCheckpoint() {
137
137
  * Checks a copied prompt against source-rendered output and reconciles only
138
138
  * exact records that the browser can positively verify.
139
139
  */
140
- export async function reconcileClipboardHandoff() {
140
+ export async function reconcileClipboardHandoff(doc = document) {
141
141
  const snapshot = checkpoint;
142
142
  if (!snapshot)
143
143
  return 0;
@@ -147,7 +147,7 @@ export async function reconcileClipboardHandoff() {
147
147
  pruneCheckpoint();
148
148
  return 0;
149
149
  }
150
- const removed = await verifyAndReconcileHandoff({ changes, structuralChanges });
150
+ const removed = await verifyAndReconcileHandoff({ changes, structuralChanges }, doc);
151
151
  pruneCheckpoint();
152
152
  if (removed > 0) {
153
153
  lastReconciledCount = removed;
@@ -175,7 +175,7 @@ export function startClipboardHandoffController(doc = document) {
175
175
  if (stopped || checking || !checkpoint)
176
176
  return;
177
177
  checking = true;
178
- void reconcileClipboardHandoff()
178
+ void reconcileClipboardHandoff(doc)
179
179
  .catch(() => undefined)
180
180
  .finally(() => {
181
181
  // Keep the guard through the MutationObserver microtask generated by
@@ -1,7 +1,8 @@
1
1
  import type { ChangeRecord } from "../changes/changesLog.js";
2
2
  import type { StructuralChange } from "../projection/structuralProjection.js";
3
+ import type { AgentSketchMetadata } from "../agent/protocol.js";
3
4
  export interface FrameworkHints {
4
5
  framework?: string;
5
6
  stylingSystem?: string;
6
7
  }
7
- export declare function generatePrompt(changes: ChangeRecord[], frameworkHints?: FrameworkHints, structuralChanges?: readonly StructuralChange[], customInstructions?: string): string;
8
+ export declare function generatePrompt(changes: ChangeRecord[], frameworkHints?: FrameworkHints, structuralChanges?: readonly StructuralChange[], customInstructions?: string, sketches?: readonly AgentSketchMetadata[]): string;
@@ -244,6 +244,17 @@ function structuralChangeLine(change) {
244
244
  ? `- Move ${target} before ${renderedInstanceDescription(before)} in ${destinationParent}.`
245
245
  : `- Move ${target} to the end of ${destinationParent}.`;
246
246
  }
247
+ function sketchLines(sketches) {
248
+ const titles = [...new Set(sketches.map((sketch) => sketch.capture.title || "(untitled)"))];
249
+ return [
250
+ ...titles.map((title) => `- Page title: ${promptText(title)}`),
251
+ ...sketches.flatMap((sketch) => [
252
+ ...(sketch.description ? [`- Description: ${promptText(sketch.description)}`] : []),
253
+ ...(sketch.annotations ?? []).map((annotation) => `- Annotation ${annotation.number}: ${promptText(annotation.description)}`),
254
+ ]),
255
+ "- Interpret the attached frozen screenshot, blue strokes, and numbered annotations as visual feedback. Do not derive selectors, source coordinates, or semantics from the strokes. Use each annotation number to match the note to its numbered dot.",
256
+ ];
257
+ }
247
258
  function renderedInstanceKey(ref) {
248
259
  const { sourceSite, locator } = ref;
249
260
  return JSON.stringify([
@@ -304,10 +315,10 @@ function canonicalizeStructuralChanges(changes) {
304
315
  }
305
316
  return canonical;
306
317
  }
307
- export function generatePrompt(changes, frameworkHints, structuralChanges = [], customInstructions = DEFAULT_CUSTOM_INSTRUCTIONS) {
318
+ export function generatePrompt(changes, frameworkHints, structuralChanges = [], customInstructions = DEFAULT_CUSTOM_INSTRUCTIONS, sketches = []) {
308
319
  const deduplicated = canonicalizeChanges(changes);
309
320
  const structuralIntent = canonicalizeStructuralChanges(structuralChanges);
310
- if (deduplicated.length === 0 && structuralIntent.length === 0)
321
+ if (deduplicated.length === 0 && structuralIntent.length === 0 && sketches.length === 0)
311
322
  return EMPTY_SENTINEL;
312
323
  const tokenChanges = deduplicated.filter(isTokenChange);
313
324
  const componentChanges = deduplicated.filter(isComponentChange);
@@ -387,5 +398,11 @@ export function generatePrompt(changes, frameworkHints, structuralChanges = [],
387
398
  lines: structuralIntent.map(structuralChangeLine),
388
399
  });
389
400
  }
401
+ if (sketches.length > 0) {
402
+ sections.push({
403
+ heading: "Sketch annotations",
404
+ lines: sketchLines(sketches),
405
+ });
406
+ }
390
407
  return renderPrompt(sections, customInstructions);
391
408
  }
@@ -0,0 +1,2 @@
1
+ /** Returns whether a document is the application-free editor controller. */
2
+ export declare function isEditorShellDocument(doc?: Document): boolean;
@@ -0,0 +1,4 @@
1
+ /** Returns whether a document is the application-free editor controller. */
2
+ export function isEditorShellDocument(doc = document) {
3
+ return doc.documentElement.hasAttribute("data-nudge-ui-editor");
4
+ }
@@ -24,7 +24,7 @@ export interface NudgeUiRuntimeCapabilities {
24
24
  readonly canvas: boolean;
25
25
  /** Whether framework component inspection and prop overrides are available. */
26
26
  readonly componentSemantics: boolean;
27
- /** Whether the experimental DOM navigation section is available. */
27
+ /** Whether the DOM navigator overlay is available. */
28
28
  readonly domNavigation?: boolean;
29
29
  /**
30
30
  * Source-coordinate precision policy for prompts; every source site is
@@ -60,6 +60,10 @@ export interface NudgeUiRuntimeConfig {
60
60
  readonly componentContracts: readonly ComponentContract[];
61
61
  /** Whether this runtime is the explicit public landing-page demo. */
62
62
  readonly demo?: boolean;
63
+ /** Additional same-origin pages seeded into the public demo canvas. */
64
+ readonly demoPages?: readonly string[];
65
+ /** Labels for demo cards in display order, including the primary card. */
66
+ readonly demoCardLabels?: readonly string[];
63
67
  }
64
68
  /**
65
69
  * Validates a host-supplied configuration and fills safe defaults.
@@ -170,6 +170,8 @@ export function normalizeNudgeUiRuntimeConfig(input) {
170
170
  if (isNormalizedRuntimeConfig(input))
171
171
  return input;
172
172
  const demo = optionalDemoFlag(input);
173
+ const demoPages = optionalStringArray(input, "demoPages");
174
+ const demoCardLabels = optionalStringArray(input, "demoCardLabels");
173
175
  const normalized = cloneAndFreeze({
174
176
  projectId: requireString(input, "projectId"),
175
177
  host: requireEnum(input, "host", RUNTIME_HOSTS),
@@ -182,6 +184,8 @@ export function normalizeNudgeUiRuntimeConfig(input) {
182
184
  tokenGeneration: optionalString(input, "tokenGeneration"),
183
185
  componentContracts: optionalArray(input, "componentContracts"),
184
186
  ...(demo === true ? { demo: true } : {}),
187
+ ...(demoPages === undefined ? {} : { demoPages }),
188
+ ...(demoCardLabels === undefined ? {} : { demoCardLabels }),
185
189
  });
186
190
  normalizedRuntimeConfigs.add(normalized);
187
191
  return normalized;
@@ -3,5 +3,11 @@ export interface DescendantNode {
3
3
  element: HTMLElement;
4
4
  depth: number;
5
5
  }
6
+ export interface NavigationNode {
7
+ element: HTMLElement;
8
+ direction: "up" | "down";
9
+ depth: number;
10
+ }
6
11
  /** Returns the nearest tracked descendants, breadth-first and document-ordered. */
7
12
  export declare function computeDescendants(el: HTMLElement, maxDepth?: number, maxNodes?: number): DescendantNode[];
13
+ export declare function computeNavigationNodes(el: HTMLElement, hierarchy?: HTMLElement[]): NavigationNode[];
@@ -40,3 +40,11 @@ export function computeDescendants(el, maxDepth = 2, maxNodes = 2) {
40
40
  .sort((left, right) => left.depth - right.depth || left.order - right.order);
41
41
  return descendants.slice(0, maxNodes).map(({ element, depth }) => ({ element, depth }));
42
42
  }
43
+ export function computeNavigationNodes(el, hierarchy = computeHierarchy(el)) {
44
+ const children = computeDescendants(el, 2, 2);
45
+ const parents = hierarchy.slice(1, (children.length > 0 ? 2 : 4) + 1);
46
+ return [
47
+ ...parents.map((element, index) => ({ element, direction: "up", depth: index + 1 })),
48
+ ...children.map(({ element, depth }) => ({ element, direction: "down", depth })),
49
+ ];
50
+ }
@@ -1,6 +1,7 @@
1
1
  import { useSyncExternalStore } from "react";
2
2
  import { computeHierarchy } from "./hierarchy.js";
3
3
  import { resolveSelectionFromElement } from "./resolveSelection.js";
4
+ import { clearInlineTextDiagnosticsForSelection } from "../inline-text/inlineTextDiagnostics.js";
4
5
  let currentSelection = null;
5
6
  const EMPTY_SELECTION = [];
6
7
  let chain = [];
@@ -28,6 +29,7 @@ function getHierarchyIndex() {
28
29
  return index;
29
30
  }
30
31
  function notify() {
32
+ clearInlineTextDiagnosticsForSelection(getSelectedElements().map((element) => element.domElement));
31
33
  listeners.forEach((l) => l());
32
34
  }
33
35
  function sameComponentTargets(a, b) {
@@ -9,4 +9,4 @@ export interface SettingsDialogProps extends McpConnectionContentProps {
9
9
  readonly onOpenChange: (open: boolean) => void;
10
10
  }
11
11
  /** Provides one settings surface for prompt, MCP, and token configuration. */
12
- export declare function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onDisconnect, onCheckAgain, }: SettingsDialogProps): ReactElement;
12
+ export declare function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onTakeOver, onDisconnect, onCheckAgain, }: SettingsDialogProps): ReactElement;
@@ -6,6 +6,10 @@ import { useBrowserCssInspection } from "../inspection/useBrowserCssInspection.j
6
6
  import { McpConnectionContent, } from "../agent/McpConnectionDialog.js";
7
7
  import { PromptSettingsFields, } from "../prompt/PromptSettingsDialog.js";
8
8
  import { TokensPanel } from "../tokens/TokensPanel.js";
9
+ import { useSelectedElement } from "../selection/selectionStore.js";
10
+ import { useFocusedCardId, useSelectedCardId } from "../canvas/canvasStore.js";
11
+ import { getRegisteredFrames } from "../canvas/projection.js";
12
+ import { getBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
9
13
  const SETTINGS_SECTIONS = [
10
14
  {
11
15
  id: "instructions",
@@ -29,12 +33,18 @@ function portalContainer() {
29
33
  : null;
30
34
  }
31
35
  /** Provides one settings surface for prompt, MCP, and token configuration. */
32
- export function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onDisconnect, onCheckAgain, }) {
36
+ export function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onTakeOver, onDisconnect, onCheckAgain, }) {
33
37
  const [activeSection, setActiveSection] = useState(initialSection);
34
38
  const closeRef = useRef(null);
35
39
  const instructionsRef = useRef(null);
40
+ const selectedElement = useSelectedElement();
41
+ const selectedCardId = useSelectedCardId();
42
+ const focusedCardId = useFocusedCardId();
43
+ const activeFrame = getRegisteredFrames().get(selectedCardId ?? focusedCardId ?? "");
44
+ const activeDocument = selectedElement?.domElement.ownerDocument ?? activeFrame?.contentDocument ?? document;
36
45
  const tokenInspection = useBrowserCssInspection(null, "base", {
37
46
  includeDocumentTokens: activeSection === "tokens",
47
+ session: getBrowserCssInspection(activeDocument),
38
48
  });
39
49
  const tokenRows = tokenInspection.documentTokens?.tokens ?? [];
40
50
  const activeDefinition = SETTINGS_SECTIONS.find((section) => section.id === activeSection) ?? SETTINGS_SECTIONS[0];
@@ -42,5 +52,5 @@ export function SettingsDialog({ open, initialSection, value, onValueChange, onO
42
52
  if (open)
43
53
  setActiveSection(initialSection);
44
54
  }, [initialSection, open]);
45
- 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, 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] })] })] })] }) }));
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] })] })] })] }) }));
46
56
  }
@@ -3,7 +3,11 @@ 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
+ import { readNudgeUiEditorTarget } from "../../transport/editor.js";
6
7
  export function AppShell() {
7
8
  const canvasEnabled = useNudgeUiRuntimeConfig().capabilities.canvas;
8
- return (_jsxs(_Fragment, { children: [canvasEnabled ? _jsx(CanvasWorkspace, {}) : null, canvasEnabled ? _jsx(CanvasElementOverlay, {}) : null, _jsx(InspectorShell, {})] }));
9
+ const primaryUrl = typeof window === "undefined"
10
+ ? null
11
+ : readNudgeUiEditorTarget(window.location.href);
12
+ return (_jsxs(_Fragment, { children: [canvasEnabled ? _jsx(CanvasWorkspace, { primaryUrl: primaryUrl }) : null, canvasEnabled ? _jsx(CanvasElementOverlay, {}) : null, _jsx(InspectorShell, {})] }));
9
13
  }
@@ -1,13 +1,15 @@
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 { IconChevronDown } from "@tabler/icons-react";
4
3
  import { isElementChange, useChanges, revertChange } from "../changes/changesLog.js";
5
4
  import { StaleChangeIndicator } from "../canvas/StaleChangeIndicator.js";
6
5
  import { Button } from "../ui/Button.js";
6
+ import { Disclosure } from "../ui/Disclosure.js";
7
7
  import { presentChange } from "../changes/presentation.js";
8
8
  import { getStructuralChanges, getStructuralChangeDiagnostics, getStructuralDiagnosticRevision, revertStructuralChange, subscribeStructuralChanges, subscribeStructuralDiagnostics, } from "../projection/structuralProjection.js";
9
9
  import { getRenderedInstanceChangeDiagnostics, getRenderedInstanceDiagnosticRevision, subscribeRenderedInstanceDiagnostics, } from "../projection/renderedInstance.js";
10
10
  import { getTextContentChangeDiagnostics, getTextProjectionDiagnosticRevision, subscribeTextProjectionDiagnostics, } from "../projection/textProjection.js";
11
+ import { SketchChanges } from "../sketch/SketchChanges.js";
12
+ import { useSketchStore } from "../sketch/store.js";
11
13
  function groupChanges(changes) {
12
14
  const map = new Map();
13
15
  for (const change of changes) {
@@ -60,6 +62,8 @@ function structuralDiagnosticText(diagnostic) {
60
62
  }
61
63
  export function ChangesLog({ onClearSession }) {
62
64
  const changes = useChanges();
65
+ const sketchStore = useSketchStore();
66
+ const sketches = sketchStore.items;
63
67
  const structuralChanges = useSyncExternalStore(subscribeStructuralChanges, getStructuralChanges, getStructuralChanges);
64
68
  // Diagnostics arrive independently from structural state when a renderer
65
69
  // resolves a snapshot or its document observer detects a reconciliation.
@@ -67,26 +71,26 @@ export function ChangesLog({ onClearSession }) {
67
71
  useSyncExternalStore(subscribeRenderedInstanceDiagnostics, getRenderedInstanceDiagnosticRevision, getRenderedInstanceDiagnosticRevision);
68
72
  useSyncExternalStore(subscribeTextProjectionDiagnostics, getTextProjectionDiagnosticRevision, getTextProjectionDiagnosticRevision);
69
73
  const groups = useMemo(() => groupChanges(changes), [changes]);
70
- const total = changes.length + structuralChanges.length;
71
- if (total === 0)
74
+ const total = changes.length + structuralChanges.length + sketches.length;
75
+ if (total === 0 && sketchStore.error === null)
72
76
  return null;
73
- return (_jsxs(_Fragment, { children: [_jsxs("details", { className: "changes", "data-test": "changes-log", children: [_jsxs("summary", { className: "changes__title", "data-test": "changes-toggle", children: [_jsx("span", { className: "changes__title-label", children: "Changes" }), total > 0 ? _jsx("span", { className: "changes__count", children: total }) : null, _jsx(IconChevronDown, { className: "changes__toggle-icon", size: 15, stroke: 2, "aria-hidden": "true" })] }), _jsx("div", { className: "changes__content", children: groups.length === 0 && structuralChanges.length === 0 ? (_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) => {
74
- const presentation = presentChange(change);
75
- const instanceDiagnostics = isElementChange(change) && change.scope === "rendered-instance"
76
- && change.instanceOverride
77
- ? getRenderedInstanceChangeDiagnostics(change.instanceOverride.id)
78
- : [];
79
- const textDiagnostics = change.kind === "text-content"
80
- ? getTextContentChangeDiagnostics(change.id)
81
- : [];
82
- 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}`));
83
- })] }, 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"
84
- ? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
85
- ? `${change.presentation.sourceParentTag} position ${change.presentation.fromIndex + 1}`
86
- : `${structuralParentLabel(change, "source")} position ${change.presentation.fromIndex + 1}`
87
- : "Visible" }), _jsx("span", { className: "changes__arrow", children: "\u2192" }), _jsx("span", { className: "changes__after", children: change.kind === "move"
88
- ? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
89
- ? `${change.presentation.destinationParentTag} position ${change.presentation.toIndex + 1}`
90
- : `${structuralParentLabel(change, "destination")} position ${change.presentation.toIndex + 1}`
91
- : "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)))] })) })] }), 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] }));
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] }));
92
96
  }
@@ -13,16 +13,22 @@ import { getStructuralChanges, subscribeStructuralChanges } from "../projection/
13
13
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
14
14
  import { createPromptRevision, getAgentClient, useAgentClient, } from "../agent/client.js";
15
15
  import { createAgentPresentationAdapter } from "../canvas/agentPresentation.js";
16
- import { discardAgentDispatch, getAgentDispatchSize, recordAgentDispatch, verifyAndReconcileAgentDispatch, } from "../agent/verification.js";
16
+ import { discardAgentDispatch, recordAgentDispatch, verifyAndReconcileAgentDispatch, } from "../agent/verification.js";
17
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";
20
+ import { SketchLayersPanel } from "../sketch/SketchLayersPanel.js";
21
+ import { isSendPromptShortcut, SEND_PROMPT_HOTKEY_EVENT } from "./shortcuts.js";
18
22
  export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, settingsSection: controlledSettingsSection, onOpenSettings, onSettingsOpenChange, } = {}) {
19
23
  const changes = useChanges();
24
+ const sketches = useSketches();
25
+ const pendingSketches = sketches.filter((item) => item.status === "pending");
20
26
  const structuralChanges = useSyncExternalStore(subscribeStructuralChanges, getStructuralChanges, getStructuralChanges);
21
27
  useSyncExternalStore(subscribeClipboardHandoff, getClipboardHandoffRevision, getClipboardHandoffRevision);
22
28
  const reconciledCount = getLastClipboardReconciledCount();
23
29
  const [copied, setCopied] = useState(false);
24
30
  const [agentCompletionStatus, setAgentCompletionStatus] = useState(null);
25
- const [agentCompletionSent, setAgentCompletionSent] = useState(0);
31
+ const [sketchFallback, setSketchFallback] = useState(null);
26
32
  const [localSettingsOpen, setLocalSettingsOpen] = useState(false);
27
33
  const [localSettingsSection, setLocalSettingsSection] = useState("instructions");
28
34
  const settingsOpen = controlledSettingsOpen ?? localSettingsOpen;
@@ -33,7 +39,8 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
33
39
  const agent = useAgentClient(runtimeConfig.projectId, agentClient);
34
40
  const agentRef = useRef(agent);
35
41
  agentRef.current = agent;
36
- const hasChanges = changes.length + structuralChanges.length > 0;
42
+ const hasChanges = changes.length + structuralChanges.length + pendingSketches.length > 0;
43
+ const changeCount = changes.length + structuralChanges.length + sketches.length;
37
44
  useEffect(() => {
38
45
  setCustomInstructions(loadCustomInstructions(runtimeConfig.projectId));
39
46
  }, [runtimeConfig.projectId]);
@@ -52,7 +59,6 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
52
59
  const revision = agent.request?.changeRevision;
53
60
  if (agent.state !== "completed" || revision === undefined)
54
61
  return;
55
- const sent = getAgentDispatchSize(revision);
56
62
  let active = true;
57
63
  void verifyAndReconcileAgentDispatch(revision)
58
64
  .then((removed) => {
@@ -61,8 +67,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
61
67
  const latest = agentRef.current;
62
68
  if (latest.request?.changeRevision !== revision || latest.state !== "completed")
63
69
  return;
64
- setAgentCompletionSent(sent);
65
- setAgentCompletionStatus(removed > 0 ? "verified" : "completed");
70
+ setAgentCompletionStatus(removed > 0 ? "verified" : null);
66
71
  })
67
72
  .catch(() => {
68
73
  if (!active)
@@ -70,13 +75,18 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
70
75
  const latest = agentRef.current;
71
76
  if (latest.request?.changeRevision !== revision || latest.state !== "completed")
72
77
  return;
73
- setAgentCompletionSent(sent);
74
- setAgentCompletionStatus("completed");
78
+ setAgentCompletionStatus(null);
75
79
  });
76
80
  return () => {
77
81
  active = false;
78
82
  };
79
83
  }, [agent.request?.changeRevision, agent.state]);
84
+ useEffect(() => {
85
+ const request = agent.request;
86
+ if (!request?.sketches || request.sketches.length === 0)
87
+ return;
88
+ void settleSketchDispatch(request.changeRevision, request.status, request.requestId, request.clientDispatchId).catch(() => undefined);
89
+ }, [agent.request?.changeRevision, agent.request?.requestId, agent.request?.clientDispatchId, agent.request?.sketches, agent.request?.status]);
80
90
  const connecting = agent.state === "pairing";
81
91
  const working = agent.state === "working" || agent.request?.status === "working";
82
92
  const canConnect = agent.companionReachable
@@ -105,7 +115,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
105
115
  ? _jsx(IconPlugConnected, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
106
116
  : canSend || working
107
117
  ? _jsx(IconSend, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
108
- : _jsx(IconCopy, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" });
118
+ : _jsx(IconCopy, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" });
109
119
  async function onClick() {
110
120
  if (disabled)
111
121
  return;
@@ -117,22 +127,83 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
117
127
  framework: runtimeConfig.framework,
118
128
  stylingSystem: runtimeConfig.stylingSystem,
119
129
  };
120
- const text = generatePrompt(changes, hints, structuralChanges, customInstructions);
130
+ const sketchHandoff = createSketchHandoffSnapshot(pendingSketches);
131
+ const sketchMetadata = sketchHandoff ? sketchMetadataForHandoff(sketchHandoff) : [];
132
+ const text = generatePrompt(changes, hints, structuralChanges, customInstructions, sketchMetadata);
121
133
  setAgentCompletionStatus(null);
122
- setAgentCompletionSent(0);
134
+ setSketchFallback(null);
123
135
  if (canSend) {
124
- const revision = createPromptRevision(changes, structuralChanges);
136
+ const revision = createPromptRevision(changes, structuralChanges, sketchMetadata);
125
137
  recordAgentDispatch(revision, changes, structuralChanges);
126
- const response = await agentClient.dispatchPrompt(text, revision);
127
- if (response)
128
- return;
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;
154
+ }
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;
166
+ }
167
+ }
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;
177
+ }
178
+ }
129
179
  discardAgentDispatch(revision);
130
180
  }
131
181
  await copyToClipboard(text);
132
- recordClipboardHandoff(changes, structuralChanges);
182
+ if (changes.length > 0 || structuralChanges.length > 0) {
183
+ recordClipboardHandoff(changes, structuralChanges);
184
+ }
133
185
  setCopied(true);
134
186
  window.setTimeout(() => setCopied(false), 1500);
135
187
  }
188
+ useEffect(() => {
189
+ function onKeydown(event) {
190
+ if (!isSendPromptShortcut(event) || !canSend || !hasChanges || disabled)
191
+ return;
192
+ event.preventDefault();
193
+ void onClick();
194
+ }
195
+ function onRendererHotkey() {
196
+ if (!canSend || !hasChanges || disabled)
197
+ return;
198
+ void onClick();
199
+ }
200
+ window.addEventListener("keydown", onKeydown, true);
201
+ window.addEventListener(SEND_PROMPT_HOTKEY_EVENT, onRendererHotkey);
202
+ return () => {
203
+ window.removeEventListener("keydown", onKeydown, true);
204
+ window.removeEventListener(SEND_PROMPT_HOTKEY_EVENT, onRendererHotkey);
205
+ };
206
+ }, [canSend, disabled, hasChanges, onClick]);
136
207
  function handleCustomInstructionsChange(value) {
137
208
  setCustomInstructions(value);
138
209
  saveCustomInstructions(runtimeConfig.projectId, value);
@@ -153,7 +224,19 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
153
224
  function openMcpConnection() {
154
225
  openSettings("mcp");
155
226
  }
156
- 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, "aria-busy": working || connecting ? "true" : undefined, title: agent.error, onClick: onClick, children: [icon, label] }), statusAction ? (_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 }), _jsx(Button, { size: "compact", variant: "quiet", "data-test": "agent-status-action", "data-action": statusAction.kind, type: "button", onClick: openMcpConnection, children: statusAction.label })] }) })) : agentStatus.kind === "listening" ? (_jsxs("div", { className: "copy-prompt__agent-status copy-prompt__agent-status--listening", "data-test": "agent-connection-status", role: "status", children: [_jsx(IconCheck, { size: "var(--icon-size-small)", stroke: 2, "aria-hidden": "true" }), _jsx("span", { children: agentStatus.label })] })) : null, reconciledCount > 0 ? (_jsxs("p", { className: "copy-prompt__hint", "data-test": "clipboard-reconciled-hint", role: "status", children: ["Removed ", reconciledCount, " implemented ", reconciledCount === 1 ? "change" : "changes", " from the next prompt."] })) : null, agentCompletionStatus === "verified" ? (_jsx("p", { className: "copy-prompt__hint", "data-test": "agent-verified-hint", role: "status", children: "Agent changes verified." })) : agentCompletionStatus === "completed" ? (_jsx("p", { className: "copy-prompt__hint", "data-test": "agent-completed-hint", role: "status", children: agentCompletionSent > 0
157
- ? "Agent completed. Remaining edits were preserved because they were not verified."
158
- : "Agent completed." })) : 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(), onDisconnect: () => agentClient.disconnect(), onCheckAgain: () => agentClient.checkConnection() })] }));
227
+ async function handleAgentStatusAction() {
228
+ if (statusAction?.kind === "takeover") {
229
+ await agentClient.takeOver();
230
+ return;
231
+ }
232
+ openMcpConnection();
233
+ }
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);
241
+ }, 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() })] }));
159
242
  }
@@ -11,5 +11,5 @@ export function EmptyState() {
11
11
  const modifierName = macPlatform ? "Command" : "Control";
12
12
  const optionKey = macPlatform ? "⌥" : "Alt";
13
13
  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: "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" }) })] })] }) })] }));
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
15
  }
@@ -7,5 +7,4 @@ declare global {
7
7
  }
8
8
  export { toggleInspector, setInspectorOpen };
9
9
  export type { SelectedElement } from "../selection/selectionStore.js";
10
- export declare function setInspectorHost(host: HTMLElement | null): void;
11
10
  export declare function InspectorShell(): ReactElement;