nudge-ui 0.2.2 → 0.2.4

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 (244) hide show
  1. package/README.md +164 -46
  2. package/dist/client.mjs +260 -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 +15 -7
  26. package/dist/inspector/agent/verification.js +113 -41
  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 +135 -38
  31. package/dist/inspector/canvas/CanvasElementOverlay.js +166 -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 +270 -122
  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/frameScrollbarStyles.d.ts +3 -0
  48. package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
  49. package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -1
  50. package/dist/inspector/canvas/keyboardShortcuts.js +4 -4
  51. package/dist/inspector/canvas/layout.d.ts +52 -0
  52. package/dist/inspector/canvas/layout.js +106 -0
  53. package/dist/inspector/canvas/projection.d.ts +15 -5
  54. package/dist/inspector/canvas/projection.js +69 -13
  55. package/dist/inspector/canvas/rendererBootstrap.js +32 -4
  56. package/dist/inspector/canvas/rendererElementSelector.js +47 -6
  57. package/dist/inspector/canvas/rendererStylesheet.js +2 -2
  58. package/dist/inspector/canvas/resizeHandleScale.js +1 -1
  59. package/dist/inspector/canvas/sessionStore.d.ts +11 -26
  60. package/dist/inspector/canvas/sessionStore.js +43 -183
  61. package/dist/inspector/canvas/staleChangeDetector.js +9 -3
  62. package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
  63. package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
  64. package/dist/inspector/canvas/viewStore.d.ts +38 -0
  65. package/dist/inspector/canvas/viewStore.js +104 -0
  66. package/dist/inspector/canvas/wheelPan.d.ts +8 -0
  67. package/dist/inspector/canvas/wheelPan.js +8 -0
  68. package/dist/inspector/changes/changesLog.d.ts +9 -6
  69. package/dist/inspector/changes/changesLog.js +48 -32
  70. package/dist/inspector/changes/codecs.d.ts +1 -0
  71. package/dist/inspector/changes/codecs.js +10 -1
  72. package/dist/inspector/changes/draftChanges.d.ts +58 -0
  73. package/dist/inspector/changes/draftChanges.js +304 -0
  74. package/dist/inspector/changes/editModel.d.ts +10 -0
  75. package/dist/inspector/changes/editModel.js +9 -2
  76. package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
  77. package/dist/inspector/changes/previewDiagnostics.js +11 -25
  78. package/dist/inspector/changes/types.d.ts +7 -0
  79. package/dist/inspector/clientManifest.d.ts +1 -1
  80. package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
  81. package/dist/inspector/comments/CommentsOverlay.js +250 -0
  82. package/dist/inspector/comments/element.d.ts +6 -0
  83. package/dist/inspector/comments/element.js +54 -0
  84. package/dist/inspector/comments/store.d.ts +29 -0
  85. package/dist/inspector/comments/store.js +121 -0
  86. package/dist/inspector/comments/verification.d.ts +5 -0
  87. package/dist/inspector/comments/verification.js +52 -0
  88. package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
  89. package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
  90. package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
  91. package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
  92. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
  93. package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
  94. package/dist/inspector/componentSemantics/types.d.ts +11 -0
  95. package/dist/inspector/drafts/model.d.ts +35 -0
  96. package/dist/inspector/drafts/model.js +45 -0
  97. package/dist/inspector/drafts/store.d.ts +28 -0
  98. package/dist/inspector/drafts/store.js +231 -0
  99. package/dist/inspector/index.js +20 -27
  100. package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
  101. package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
  102. package/dist/inspector/iterations/capture.d.ts +2 -0
  103. package/dist/inspector/iterations/capture.js +174 -0
  104. package/dist/inspector/iterations/client.d.ts +7 -0
  105. package/dist/inspector/iterations/client.js +52 -0
  106. package/dist/inspector/iterations/coordinator.d.ts +15 -0
  107. package/dist/inspector/iterations/coordinator.js +130 -0
  108. package/dist/inspector/iterations/prompt.d.ts +3 -0
  109. package/dist/inspector/iterations/prompt.js +26 -0
  110. package/dist/inspector/overlay/DomNavigator.js +21 -15
  111. package/dist/inspector/overlay/interactionStyles.js +3 -1
  112. package/dist/inspector/overlay/spacingGestures.d.ts +13 -1
  113. package/dist/inspector/overlay/spacingGestures.js +82 -5
  114. package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
  115. package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
  116. package/dist/inspector/projection/renderedInstance.d.ts +17 -3
  117. package/dist/inspector/projection/renderedInstance.js +62 -11
  118. package/dist/inspector/projection/structuralProjection.d.ts +2 -1
  119. package/dist/inspector/projection/structuralProjection.js +21 -17
  120. package/dist/inspector/projection/textProjection.d.ts +2 -1
  121. package/dist/inspector/projection/textProjection.js +64 -15
  122. package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
  123. package/dist/inspector/prompt/clipboardHandoff.d.ts +15 -18
  124. package/dist/inspector/prompt/clipboardHandoff.js +97 -143
  125. package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
  126. package/dist/inspector/prompt/generatePrompt.js +22 -17
  127. package/dist/inspector/runtime/runtimeConfig.d.ts +9 -0
  128. package/dist/inspector/runtime/runtimeConfig.js +24 -0
  129. package/dist/inspector/selection/sourceSite.d.ts +12 -0
  130. package/dist/inspector/selection/sourceSite.js +28 -0
  131. package/dist/inspector/settings/SettingsDialog.js +21 -8
  132. package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
  133. package/dist/inspector/shell/AgentStatusButton.js +36 -0
  134. package/dist/inspector/shell/AppShell.js +4 -2
  135. package/dist/inspector/shell/ChangesLog.js +46 -26
  136. package/dist/inspector/shell/CopyPromptButton.js +184 -93
  137. package/dist/inspector/shell/EmptyState.js +9 -8
  138. package/dist/inspector/shell/InspectorShell.js +13 -21
  139. package/dist/inspector/shell/PeekOriginalButton.js +24 -23
  140. package/dist/inspector/shell/originalPreview.js +5 -5
  141. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  142. package/dist/inspector/shell/shortcuts.js +11 -0
  143. package/dist/inspector/sketch/SketchEditor.js +14 -14
  144. package/dist/inspector/sketch/SketchEntryButton.js +1 -1
  145. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
  146. package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
  147. package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
  148. package/dist/inspector/sketch/SketchOverlay.js +7 -7
  149. package/dist/inspector/sketch/SketchWorkspace.js +18 -12
  150. package/dist/inspector/sketch/interaction.d.ts +1 -0
  151. package/dist/inspector/sketch/interaction.js +8 -4
  152. package/dist/inspector/sketch/model.d.ts +5 -0
  153. package/dist/inspector/sketch/model.js +6 -2
  154. package/dist/inspector/sketch/raster.d.ts +1 -2
  155. package/dist/inspector/sketch/raster.js +68 -4
  156. package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
  157. package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
  158. package/dist/inspector/styleEditors/BorderEditor.js +49 -78
  159. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
  160. package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
  161. package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
  162. package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
  163. package/dist/inspector/styleEditors/ColorPicker.js +10 -4
  164. package/dist/inspector/styleEditors/GapField.js +3 -5
  165. package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
  166. package/dist/inspector/styleEditors/GridChildSection.js +18 -9
  167. package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
  168. package/dist/inspector/styleEditors/GridPicker.js +8 -2
  169. package/dist/inspector/styleEditors/GridSection.js +1 -1
  170. package/dist/inspector/styleEditors/GridValueField.js +19 -14
  171. package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
  172. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
  173. package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
  174. package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
  175. package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
  176. package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
  177. package/dist/inspector/styleEditors/LayoutSection.js +32 -11
  178. package/dist/inspector/styleEditors/SpacingBox.js +7 -5
  179. package/dist/inspector/styleEditors/Typography.js +11 -3
  180. package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
  181. package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
  182. package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
  183. package/dist/inspector/styleEditors/gradientValue.js +114 -0
  184. package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
  185. package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
  186. package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
  187. package/dist/inspector/styleEditors/layoutValue.js +0 -42
  188. package/dist/inspector/styleEditors/nudgeValue.d.ts +2 -2
  189. package/dist/inspector/styleEditors/nudgeValue.js +36 -8
  190. package/dist/inspector/tokens/TokenField.d.ts +7 -0
  191. package/dist/inspector/tokens/TokenField.js +35 -25
  192. package/dist/inspector/tokens/TokenLabel.js +5 -14
  193. package/dist/inspector/tokens/editActions.d.ts +1 -1
  194. package/dist/inspector/tokens/editActions.js +7 -1
  195. package/dist/inspector/ui/AtRuleContext.js +4 -6
  196. package/dist/inspector/ui/Button.d.ts +6 -2
  197. package/dist/inspector/ui/Button.js +4 -3
  198. package/dist/inspector/ui/ControlSurface.d.ts +2 -2
  199. package/dist/inspector/ui/ControlSurface.js +4 -3
  200. package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
  201. package/dist/inspector/ui/InlineStyleWarning.js +13 -0
  202. package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
  203. package/dist/inspector/ui/InspectorPopover.js +4 -5
  204. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  205. package/dist/inspector/ui/PopoverListbox.js +8 -7
  206. package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
  207. package/dist/inspector/ui/SegmentedControl.js +2 -2
  208. package/dist/inspector/ui/Select.d.ts +6 -1
  209. package/dist/inspector/ui/Select.js +7 -11
  210. package/dist/inspector/ui/SideValuesField.js +1 -1
  211. package/dist/inspector/ui/Tooltip.d.ts +26 -0
  212. package/dist/inspector/ui/Tooltip.js +32 -0
  213. package/dist/inspector/ui/classNames.d.ts +2 -0
  214. package/dist/inspector/ui/classNames.js +4 -0
  215. package/dist/inspector/ui/customCursors.d.ts +23 -0
  216. package/dist/inspector/ui/customCursors.js +122 -0
  217. package/dist/inspector/ui/dragCursor.d.ts +0 -2
  218. package/dist/inspector/ui/dragCursor.js +2 -10
  219. package/dist/inspector/ui/portalContainer.d.ts +2 -0
  220. package/dist/inspector/ui/portalContainer.js +6 -0
  221. package/dist/inspector/ui/styles.js +29 -21
  222. package/dist/inspector/workspace/commands.d.ts +9 -0
  223. package/dist/inspector/workspace/commands.js +82 -0
  224. package/dist/inspector/workspace/controller.d.ts +4 -0
  225. package/dist/inspector/workspace/controller.js +66 -0
  226. package/dist/inspector/workspace/handoff.d.ts +42 -0
  227. package/dist/inspector/workspace/handoff.js +115 -0
  228. package/dist/inspector/workspace/sentItems.d.ts +19 -0
  229. package/dist/inspector/workspace/sentItems.js +51 -0
  230. package/dist/inspector/workspace/timeline.d.ts +30 -0
  231. package/dist/inspector/workspace/timeline.js +108 -0
  232. package/dist/internal/agent-protocol.d.ts +19 -2
  233. package/dist/internal/agent-protocol.js +13 -0
  234. package/dist/nudge-ui.mjs +558 -311
  235. package/dist/project/artifacts.d.ts +13 -0
  236. package/dist/project/artifacts.js +253 -0
  237. package/dist/transport/artifacts.d.ts +8 -0
  238. package/dist/transport/artifacts.js +9 -0
  239. package/dist/transport/editor.js +3 -1
  240. package/dist/transport/routes.d.ts +2 -0
  241. package/dist/transport/routes.js +2 -0
  242. package/package.json +7 -3
  243. package/dist/inspector/changes/workspaceChanges.d.ts +0 -47
  244. package/dist/inspector/changes/workspaceChanges.js +0 -265
@@ -1,3 +1,4 @@
1
+ import { getActiveDraftChanges, getDraftChanges } from "../changes/draftChanges.js";
1
2
  import { sourceSiteSelector } from "../selection/sourceSite.js";
2
3
  import { isNudgeUiDev } from "../runtime/devFlag.js";
3
4
  import { isTextContentChangeValue, isTextProjectionTargetValue, } from "../inline-text/textChangeBoundary.js";
@@ -48,6 +49,39 @@ function isEmptyTextAffordance(node) {
48
49
  function hasInlineTextEditor(element) {
49
50
  return element.querySelector('[data-inline-editor="true"]') !== null;
50
51
  }
52
+ /**
53
+ * What the application last rendered into each projected Text node. A source
54
+ * update can render the projected value itself, so restoring `before` would
55
+ * overwrite real application output that the framework will not repaint.
56
+ */
57
+ let applicationText = new WeakMap();
58
+ function isInlineEditorNode(node) {
59
+ return node.nodeType === 1 && (node.matches('[data-inline-editor="true"]')
60
+ || hasInlineTextEditor(node));
61
+ }
62
+ function recordApplicationWrites(records) {
63
+ // The inline editor swaps its wrapper back for the original node, writing
64
+ // the draft, in one task. That write belongs to the editor.
65
+ if (records.some((record) => record.type === "childList"
66
+ && [...record.addedNodes, ...record.removedNodes].some(isInlineEditorNode)))
67
+ return;
68
+ for (const record of records) {
69
+ if (record.type !== "characterData" || !isTextNode(record.target))
70
+ continue;
71
+ if (applicationText.has(record.target))
72
+ applicationText.set(record.target, record.target.nodeValue ?? "");
73
+ }
74
+ }
75
+ /** Writes projection-owned text without mistaking it for an application render. */
76
+ function writeProjectedText(state, node, value) {
77
+ const pending = state.observer?.takeRecords() ?? [];
78
+ if (pending.length > 0) {
79
+ recordApplicationWrites(pending);
80
+ scheduleValidation(state.doc, state);
81
+ }
82
+ node.nodeValue = value;
83
+ state.observer?.takeRecords();
84
+ }
51
85
  function readProjectionMarkers(element) {
52
86
  const raw = element.getAttribute(TEXT_PROJECTION_ATTR);
53
87
  if (!raw)
@@ -410,7 +444,7 @@ function textProjectionIdentityMatches(element, change) {
410
444
  && element.getAttribute("data-cprops") === change.target.props
411
445
  && element.getAttribute("aria-label") === change.target.ariaLabel;
412
446
  }
413
- function restoreAppliedProjection(applied) {
447
+ function restoreAppliedProjection(state, applied) {
414
448
  const element = applied.element;
415
449
  if (!element || !element.isConnected)
416
450
  return;
@@ -425,7 +459,9 @@ function restoreAppliedProjection(applied) {
425
459
  if (!textHostIsSafe(element, applied.change.after.length === 0, applied.change.target))
426
460
  return;
427
461
  if (textNode) {
428
- textNode.nodeValue = applied.change.before;
462
+ const rendered = applicationText.get(textNode);
463
+ applicationText.delete(textNode);
464
+ writeProjectedText(state, textNode, rendered ?? applied.change.before);
429
465
  }
430
466
  else if (applied.change.target.textNodePath === undefined
431
467
  && applied.change.after.length === 0
@@ -466,7 +502,7 @@ function validationStatus(applied) {
466
502
  * mutates; an overridden projection remains authoritative and is never
467
503
  * re-applied.
468
504
  */
469
- function resolvePendingProjection(doc, applied) {
505
+ function resolvePendingProjection(doc, state, applied) {
470
506
  if (applied.status !== "missing" && applied.status !== "ambiguous") {
471
507
  return validationStatus(applied);
472
508
  }
@@ -483,7 +519,9 @@ function resolvePendingProjection(doc, applied) {
483
519
  element.textContent = applied.change.after;
484
520
  }
485
521
  else {
486
- textNode.nodeValue = applied.change.after;
522
+ if (!applicationText.has(textNode))
523
+ applicationText.set(textNode, textNode.nodeValue ?? "");
524
+ writeProjectedText(state, textNode, applied.change.after);
487
525
  }
488
526
  applied.element = element;
489
527
  return "applied";
@@ -491,7 +529,7 @@ function resolvePendingProjection(doc, applied) {
491
529
  function validateAppliedProjection(doc, state) {
492
530
  let changed = false;
493
531
  for (const applied of state.applied.values()) {
494
- const status = resolvePendingProjection(doc, applied);
532
+ const status = resolvePendingProjection(doc, state, applied);
495
533
  if (status !== applied.status) {
496
534
  applied.status = status;
497
535
  changed = true;
@@ -515,6 +553,7 @@ function getDocumentState(doc) {
515
553
  if (existing)
516
554
  return existing;
517
555
  const state = {
556
+ doc,
518
557
  applied: new Map(),
519
558
  emptyAffordances: new Map(),
520
559
  snapshotKey: null,
@@ -523,7 +562,10 @@ function getDocumentState(doc) {
523
562
  };
524
563
  const Observer = doc.defaultView?.MutationObserver;
525
564
  if (Observer && doc.documentElement) {
526
- state.observer = new Observer(() => scheduleValidation(doc, state));
565
+ state.observer = new Observer((records) => {
566
+ recordApplicationWrites(records);
567
+ scheduleValidation(doc, state);
568
+ });
527
569
  state.observer.observe(doc.documentElement, {
528
570
  childList: true,
529
571
  subtree: true,
@@ -570,7 +612,7 @@ export function applyTextContentProjection(doc, changes) {
570
612
  if (incomingIds.has(id))
571
613
  continue;
572
614
  removeEmptyTextAffordance(state, id);
573
- restoreAppliedProjection(applied);
615
+ restoreAppliedProjection(state, applied);
574
616
  if (applied.element)
575
617
  removeProjectionMarker(applied.element, applied.change.target, id);
576
618
  }
@@ -578,13 +620,13 @@ export function applyTextContentProjection(doc, changes) {
578
620
  for (const change of changes) {
579
621
  const existing = previous.get(change.id);
580
622
  if (existing && JSON.stringify(existing.change) === JSON.stringify(change)) {
581
- existing.status = resolvePendingProjection(doc, existing);
623
+ existing.status = resolvePendingProjection(doc, state, existing);
582
624
  next.set(change.id, existing);
583
625
  continue;
584
626
  }
585
627
  if (existing) {
586
628
  removeEmptyTextAffordance(state, change.id);
587
- restoreAppliedProjection(existing);
629
+ restoreAppliedProjection(state, existing);
588
630
  if (existing.element)
589
631
  removeProjectionMarker(existing.element, existing.change.target, change.id);
590
632
  }
@@ -602,8 +644,11 @@ export function applyTextContentProjection(doc, changes) {
602
644
  next.set(change.id, { change, element: null, status: "missing" });
603
645
  continue;
604
646
  }
605
- if (textNode)
606
- textNode.nodeValue = change.after;
647
+ if (textNode) {
648
+ if (!applicationText.has(textNode))
649
+ applicationText.set(textNode, textNode.nodeValue ?? "");
650
+ writeProjectedText(state, textNode, change.after);
651
+ }
607
652
  else
608
653
  element.textContent = change.after;
609
654
  next.set(change.id, { change, element, status: "applied" });
@@ -640,10 +685,10 @@ export function subscribeTextProjectionDiagnostics(listener) {
640
685
  export function getTextProjectionDiagnosticRevision() {
641
686
  return diagnosticRevision;
642
687
  }
643
- export function recordCanvasTextProjectionReports(cardId, revision, reports) {
688
+ export function recordCanvasTextProjectionReports(cardId, revision, reports, projection) {
644
689
  if (!Number.isSafeInteger(revision) || revision < 0 || !reports.every(isTextProjectionReport))
645
690
  return;
646
- const expected = new Set(canonicalChanges.keys());
691
+ const expected = projection ? new Set(projection.textContentChanges.map((record) => record.id)) : new Set(canonicalChanges.keys());
647
692
  if (reports.length !== expected.size
648
693
  || new Set(reports.map((report) => report.changeId)).size !== reports.length
649
694
  || reports.some((report) => !expected.has(report.changeId)))
@@ -654,7 +699,8 @@ export function recordCanvasTextProjectionReports(cardId, revision, reports) {
654
699
  const next = reports.map((report) => ({ ...report }));
655
700
  if (existing && existing.revision === revision && sameReports(existing.reports, next))
656
701
  return;
657
- reportsByCanvasCard.set(cardId, { revision, reports: next });
702
+ const current = getActiveDraftChanges();
703
+ reportsByCanvasCard.set(cardId, { revision, reports: next, draftId: projection?.draftId ?? current.draftId, draftRevision: projection?.draftRevision ?? current.revision });
658
704
  notifyDiagnostics();
659
705
  }
660
706
  export function clearCanvasTextProjectionReports(cardId) {
@@ -664,7 +710,7 @@ export function clearCanvasTextProjectionReports(cardId) {
664
710
  }
665
711
  export function getTextContentChangeDiagnostics(changeId) {
666
712
  const diagnostics = [];
667
- const change = canonicalChanges.get(changeId);
713
+ const change = getActiveDraftChanges().changes.find((change) => change.kind === "text-content" && change.id === changeId) ?? canonicalChanges.get(changeId);
668
714
  const host = reportsByDocument.get(document)?.find((report) => report.changeId === changeId);
669
715
  if (host)
670
716
  diagnostics.push({
@@ -674,6 +720,8 @@ export function getTextContentChangeDiagnostics(changeId) {
674
720
  evidence: change?.evidence,
675
721
  });
676
722
  for (const [cardId, entry] of reportsByCanvasCard) {
723
+ if (entry.draftId !== getActiveDraftChanges().draftId || entry.draftRevision !== getDraftChanges(entry.draftId).revision)
724
+ continue;
677
725
  const report = entry.reports.find((candidate) => candidate.changeId === changeId);
678
726
  if (report)
679
727
  diagnostics.push({
@@ -704,6 +752,7 @@ export function resetTextProjectionState() {
704
752
  }
705
753
  }
706
754
  documentStates = new Map();
755
+ applicationText = new WeakMap();
707
756
  canonicalChanges = new Map();
708
757
  reportsByDocument = new WeakMap();
709
758
  reportsByCanvasCard.clear();
@@ -2,11 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { useRef } from "react";
3
3
  import { Dialog } from "@base-ui/react/dialog";
4
4
  import { IconX } from "@tabler/icons-react";
5
- function portalContainer() {
6
- return typeof document !== "undefined"
7
- ? document.getElementById("nudge-ui-root")?.shadowRoot ?? document.body
8
- : null;
9
- }
5
+ import { portalContainer } from "../ui/portalContainer.js";
10
6
  /** Edits the instructions appended to every generated prompt. */
11
7
  export function PromptSettingsFields({ value, onChange, instructionsRef, }) {
12
8
  return (_jsxs(_Fragment, { children: [_jsx("div", { className: "prompt-settings__field", children: _jsx("textarea", { ref: instructionsRef, id: "nudge-ui-custom-instructions", className: "prompt-settings__textarea", "data-test": "prompt-custom-instructions", "aria-label": "Custom instructions", rows: 6, value: value, onChange: (event) => onChange(event.currentTarget.value) }) }), _jsx("div", { className: "prompt-settings__actions", children: _jsx(Dialog.Close, { className: "button button--primary", "data-test": "prompt-settings-done", type: "button", children: "Done" }) })] }));
@@ -1,34 +1,31 @@
1
- import { type ChangeRecord } from "../changes/changesLog.js";
2
- import { type StructuralChange } from "../projection/structuralProjection.js";
1
+ import type { ChangeRecord } from "../changes/types.js";
2
+ import type { StructuralChange } from "../changes/structuralTypes.js";
3
+ import { type HandoffOwner } from "../agent/verification.js";
3
4
  export interface ClipboardHandoffFingerprint {
4
5
  readonly key: string;
5
6
  readonly fingerprint: string;
6
7
  }
7
- export interface ClipboardHandoffSnapshot {
8
+ interface ClipboardDraftHandoff {
9
+ readonly owner: HandoffOwner;
8
10
  readonly changes: readonly ClipboardHandoffFingerprint[];
9
11
  readonly structuralChanges: readonly ClipboardHandoffFingerprint[];
10
12
  }
11
- /** Validates an origin-local persisted clipboard checkpoint. */
13
+ export interface ClipboardHandoffSnapshot {
14
+ readonly drafts: readonly ClipboardDraftHandoff[];
15
+ }
12
16
  export declare function isClipboardHandoffSnapshot(value: unknown): value is ClipboardHandoffSnapshot | null;
13
- /** Captures the exact records included in a successful clipboard copy. */
14
- export declare function recordClipboardHandoff(changes: readonly ChangeRecord[], structuralChanges?: readonly StructuralChange[]): void;
15
- /** Returns a defensive copy for durable-session serialization. */
17
+ export declare function recordClipboardHandoff(changes: readonly ChangeRecord[], structuralChanges?: readonly StructuralChange[], owner?: HandoffOwner): void;
16
18
  export declare function getClipboardHandoffSnapshot(): ClipboardHandoffSnapshot | null;
17
- /** Replaces the in-memory checkpoint from a validated durable session. */
18
19
  export declare function hydrateClipboardHandoff(value: ClipboardHandoffSnapshot | null): void;
19
20
  export declare function clearClipboardHandoff(): void;
20
21
  export declare function subscribeClipboardHandoff(listener: () => void): () => void;
21
- /** Monotonic external-store snapshot for persistence and React consumers. */
22
22
  export declare function getClipboardHandoffRevision(): number;
23
23
  export declare function getLastClipboardReconciledCount(): number;
24
- /**
25
- * Checks a copied prompt against source-rendered output and reconciles only
26
- * exact records that the browser can positively verify.
27
- */
24
+ /** Records in one draft that still equal its copied, unverified snapshot. */
25
+ export declare function getClipboardHandoffRecords(draftId: string): {
26
+ changes: ChangeRecord[];
27
+ structuralChanges: StructuralChange[];
28
+ };
28
29
  export declare function reconcileClipboardHandoff(doc?: Document): Promise<number>;
29
- /**
30
- * Watches source-driven document refreshes and page-return signals. The
31
- * trailing debounce lets an HMR commit settle before verification lifts the
32
- * inspector preview for two frames.
33
- */
34
30
  export declare function startClipboardHandoffController(doc?: Document): () => void;
31
+ export {};
@@ -1,212 +1,166 @@
1
1
  import { changeKey } from "../changes/model.js";
2
- import { subscribeChanges, } from "../changes/changesLog.js";
3
- import {} from "../projection/structuralProjection.js";
4
- import { getWorkspaceChanges } from "../changes/workspaceChanges.js";
2
+ import { getDraftChanges, getActiveDraftChanges, subscribeActiveDraftChanges } from "../changes/draftChanges.js";
5
3
  import { documentRevisions, subscribeDocumentRevision } from "../tokens/resolution/cssomCollector.js";
6
- import { handoffChangeFingerprint, handoffStructuralFingerprint, verifyAndReconcileHandoff, } from "../agent/verification.js";
7
- const MAX_HANDOFF_RECORDS = 10_000;
8
- const MAX_KEY_LENGTH = 16_384;
9
- const MAX_FINGERPRINT_LENGTH = 1_048_576;
10
- const RECONCILE_DEBOUNCE_MS = 450;
11
- let checkpoint = null;
4
+ import { captureHandoffOwner, handoffChangeFingerprint, handoffStructuralFingerprint, verifyAndReconcileHandoff, } from "../agent/verification.js";
5
+ import { isDraftTarget } from "../drafts/model.js";
6
+ const checkpoints = new Map();
12
7
  let revision = 0;
13
- let lastReconciledCount = 0;
8
+ const reconciledCounts = new Map();
14
9
  const listeners = new Set();
15
10
  function notify() {
16
11
  revision += 1;
17
12
  for (const listener of listeners)
18
13
  listener();
19
14
  }
20
- function cloneSnapshot(snapshot) {
21
- return {
22
- changes: snapshot.changes.map((entry) => ({ ...entry })),
23
- structuralChanges: snapshot.structuralChanges.map((entry) => ({ ...entry })),
24
- };
25
- }
26
15
  function isFingerprint(value) {
27
- if (!value || typeof value !== "object" || Array.isArray(value))
16
+ if (!value || typeof value !== "object")
28
17
  return false;
29
18
  const entry = value;
30
- return Object.keys(entry).every((key) => key === "key" || key === "fingerprint")
31
- && typeof entry.key === "string"
32
- && entry.key.length > 0
33
- && entry.key.length <= MAX_KEY_LENGTH
34
- && typeof entry.fingerprint === "string"
35
- && entry.fingerprint.length > 0
36
- && entry.fingerprint.length <= MAX_FINGERPRINT_LENGTH;
19
+ return typeof entry.key === "string" && entry.key.length > 0 && entry.key.length <= 16_384
20
+ && typeof entry.fingerprint === "string" && entry.fingerprint.length > 0 && entry.fingerprint.length <= 1_048_576;
37
21
  }
38
- /** Validates an origin-local persisted clipboard checkpoint. */
39
22
  export function isClipboardHandoffSnapshot(value) {
40
23
  if (value === null)
41
24
  return true;
42
- if (!value || typeof value !== "object" || Array.isArray(value))
25
+ if (!value || typeof value !== "object")
43
26
  return false;
44
- const candidate = value;
45
- if (!Object.keys(candidate).every((key) => key === "changes" || key === "structuralChanges")
46
- || !Array.isArray(candidate.changes)
47
- || !Array.isArray(candidate.structuralChanges)
48
- || candidate.changes.length + candidate.structuralChanges.length > MAX_HANDOFF_RECORDS
49
- || !candidate.changes.every(isFingerprint)
50
- || !candidate.structuralChanges.every(isFingerprint)) {
27
+ const snapshot = value;
28
+ if (!Array.isArray(snapshot.drafts) || snapshot.drafts.length > 512)
51
29
  return false;
30
+ const ids = new Set();
31
+ for (const draft of snapshot.drafts) {
32
+ const owner = draft?.owner;
33
+ if (!owner || typeof owner.draftId !== "string" || ids.has(owner.draftId)
34
+ || !Number.isSafeInteger(owner.draftRevision) || owner.draftRevision < 0 || !isDraftTarget(owner.target)
35
+ || !Array.isArray(owner.frameIds) || owner.frameIds.some((id) => typeof id !== "string")
36
+ || !Array.isArray(draft.changes) || !Array.isArray(draft.structuralChanges)
37
+ || draft.changes.length + draft.structuralChanges.length > 10_000
38
+ || !draft.changes.every(isFingerprint) || !draft.structuralChanges.every(isFingerprint)
39
+ || new Set(draft.changes.map((entry) => entry.key)).size !== draft.changes.length
40
+ || new Set(draft.structuralChanges.map((entry) => entry.key)).size !== draft.structuralChanges.length)
41
+ return false;
42
+ ids.add(owner.draftId);
52
43
  }
53
- const changeKeys = candidate.changes.map((entry) => entry.key);
54
- const structuralKeys = candidate.structuralChanges.map((entry) => entry.key);
55
- return new Set(changeKeys).size === changeKeys.length
56
- && new Set(structuralKeys).size === structuralKeys.length;
44
+ return true;
57
45
  }
58
- /** Captures the exact records included in a successful clipboard copy. */
59
- export function recordClipboardHandoff(changes, structuralChanges = []) {
60
- checkpoint = {
61
- changes: changes.map((change) => ({
62
- key: changeKey(change),
63
- fingerprint: handoffChangeFingerprint(change),
64
- })),
65
- structuralChanges: structuralChanges.map((change) => ({
66
- key: change.id,
67
- fingerprint: handoffStructuralFingerprint(change),
68
- })),
46
+ function fingerprintSnapshot(owner, changes, structuralChanges) {
47
+ return {
48
+ owner: structuredClone(owner),
49
+ changes: changes.map((change) => ({ key: changeKey(change), fingerprint: handoffChangeFingerprint(change) })),
50
+ structuralChanges: structuralChanges.map((change) => ({ key: change.id, fingerprint: handoffStructuralFingerprint(change) })),
69
51
  };
70
- lastReconciledCount = 0;
52
+ }
53
+ export function recordClipboardHandoff(changes, structuralChanges = [], owner = captureHandoffOwner()) {
54
+ checkpoints.set(owner.draftId, fingerprintSnapshot(owner, changes, structuralChanges));
55
+ reconciledCounts.delete(owner.draftId);
71
56
  notify();
72
57
  }
73
- /** Returns a defensive copy for durable-session serialization. */
74
58
  export function getClipboardHandoffSnapshot() {
75
- return checkpoint ? cloneSnapshot(checkpoint) : null;
59
+ return checkpoints.size ? structuredClone({ drafts: [...checkpoints.values()] }) : null;
76
60
  }
77
- /** Replaces the in-memory checkpoint from a validated durable session. */
78
61
  export function hydrateClipboardHandoff(value) {
79
- if (!value && !checkpoint && lastReconciledCount === 0)
80
- return;
81
- checkpoint = value ? cloneSnapshot(value) : null;
82
- lastReconciledCount = 0;
62
+ checkpoints.clear();
63
+ for (const draft of value?.drafts ?? [])
64
+ checkpoints.set(draft.owner.draftId, structuredClone(draft));
65
+ reconciledCounts.clear();
83
66
  notify();
84
67
  }
85
68
  export function clearClipboardHandoff() {
86
- if (!checkpoint && lastReconciledCount === 0)
69
+ if (!checkpoints.size && !reconciledCounts.size)
87
70
  return;
88
- checkpoint = null;
89
- lastReconciledCount = 0;
71
+ checkpoints.clear();
72
+ reconciledCounts.clear();
90
73
  notify();
91
74
  }
92
75
  export function subscribeClipboardHandoff(listener) {
93
76
  listeners.add(listener);
94
77
  return () => listeners.delete(listener);
95
78
  }
96
- /** Monotonic external-store snapshot for persistence and React consumers. */
97
- export function getClipboardHandoffRevision() {
98
- return revision;
99
- }
100
- export function getLastClipboardReconciledCount() {
101
- return lastReconciledCount;
102
- }
103
- function matchingChanges(snapshot) {
104
- const expected = new Map(snapshot.changes.map((entry) => [entry.key, entry.fingerprint]));
105
- return getWorkspaceChanges().changes.filter((change) => expected.get(changeKey(change)) === handoffChangeFingerprint(change));
106
- }
107
- function matchingStructuralChanges(snapshot) {
108
- const expected = new Map(snapshot.structuralChanges.map((entry) => [entry.key, entry.fingerprint]));
109
- return getWorkspaceChanges().structuralChanges.filter((change) => expected.get(change.id) === handoffStructuralFingerprint(change));
79
+ export function getClipboardHandoffRevision() { return revision; }
80
+ export function getLastClipboardReconciledCount() { return reconciledCounts.get(getActiveDraftChanges().draftId) ?? 0; }
81
+ function matchingRecords(snapshot) {
82
+ const workspace = getDraftChanges(snapshot.owner.draftId);
83
+ const changes = new Map(snapshot.changes.map((entry) => [entry.key, entry.fingerprint]));
84
+ const structures = new Map(snapshot.structuralChanges.map((entry) => [entry.key, entry.fingerprint]));
85
+ return {
86
+ changes: workspace.changes.filter((change) => changes.get(changeKey(change)) === handoffChangeFingerprint(change)),
87
+ structuralChanges: workspace.structuralChanges.filter((change) => structures.get(change.id) === handoffStructuralFingerprint(change)),
88
+ };
110
89
  }
111
- /** Drops checkpoint entries that no longer describe current canonical intent. */
112
- function pruneCheckpoint() {
113
- if (!checkpoint)
114
- return false;
115
- const changes = matchingChanges(checkpoint);
116
- const structuralChanges = matchingStructuralChanges(checkpoint);
117
- if (changes.length === checkpoint.changes.length
118
- && structuralChanges.length === checkpoint.structuralChanges.length) {
119
- return false;
90
+ /** Records in one draft that still equal its copied, unverified snapshot. */
91
+ export function getClipboardHandoffRecords(draftId) {
92
+ const snapshot = checkpoints.get(draftId);
93
+ return snapshot ? matchingRecords(snapshot) : { changes: [], structuralChanges: [] };
94
+ }
95
+ function pruneCheckpoints() {
96
+ let changed = false;
97
+ for (const [id, snapshot] of checkpoints) {
98
+ const matching = matchingRecords(snapshot);
99
+ if (matching.changes.length === snapshot.changes.length && matching.structuralChanges.length === snapshot.structuralChanges.length)
100
+ continue;
101
+ changed = true;
102
+ if (matching.changes.length + matching.structuralChanges.length === 0)
103
+ checkpoints.delete(id);
104
+ else
105
+ checkpoints.set(id, fingerprintSnapshot(snapshot.owner, matching.changes, matching.structuralChanges));
120
106
  }
121
- checkpoint = changes.length + structuralChanges.length === 0
122
- ? null
123
- : {
124
- changes: changes.map((change) => ({
125
- key: changeKey(change),
126
- fingerprint: handoffChangeFingerprint(change),
127
- })),
128
- structuralChanges: structuralChanges.map((change) => ({
129
- key: change.id,
130
- fingerprint: handoffStructuralFingerprint(change),
131
- })),
132
- };
133
- notify();
134
- return true;
107
+ if (changed)
108
+ notify();
135
109
  }
136
- /**
137
- * Checks a copied prompt against source-rendered output and reconciles only
138
- * exact records that the browser can positively verify.
139
- */
140
110
  export async function reconcileClipboardHandoff(doc = document) {
141
- const snapshot = checkpoint;
142
- if (!snapshot)
143
- return 0;
144
- const changes = matchingChanges(snapshot);
145
- const structuralChanges = matchingStructuralChanges(snapshot);
146
- if (changes.length + structuralChanges.length === 0) {
147
- pruneCheckpoint();
148
- return 0;
111
+ let removed = 0;
112
+ for (const snapshot of [...checkpoints.values()]) {
113
+ const matching = matchingRecords(snapshot);
114
+ if (matching.changes.length + matching.structuralChanges.length === 0)
115
+ continue;
116
+ const count = await verifyAndReconcileHandoff({ owner: snapshot.owner, ...matching }, doc);
117
+ if (count > 0)
118
+ reconciledCounts.set(snapshot.owner.draftId, count);
119
+ removed += count;
149
120
  }
150
- const removed = await verifyAndReconcileHandoff({ changes, structuralChanges }, doc);
151
- pruneCheckpoint();
121
+ pruneCheckpoints();
152
122
  if (removed > 0) {
153
- lastReconciledCount = removed;
154
123
  notify();
155
124
  }
156
125
  return removed;
157
126
  }
158
- /**
159
- * Watches source-driven document refreshes and page-return signals. The
160
- * trailing debounce lets an HMR commit settle before verification lifts the
161
- * inspector preview for two frames.
162
- */
163
127
  export function startClipboardHandoffController(doc = document) {
164
- const ownerWindow = doc.defaultView;
128
+ const view = doc.defaultView;
165
129
  let timer = null;
166
130
  let checking = false;
167
131
  let stopped = false;
168
132
  const schedule = () => {
169
- if (stopped || checking || !checkpoint)
133
+ if (stopped || checking || !checkpoints.size)
170
134
  return;
171
135
  if (timer !== null)
172
136
  clearTimeout(timer);
173
137
  timer = setTimeout(() => {
174
138
  timer = null;
175
- if (stopped || checking || !checkpoint)
139
+ if (stopped || checking || !checkpoints.size)
176
140
  return;
177
141
  checking = true;
178
- void reconcileClipboardHandoff(doc)
179
- .catch(() => undefined)
180
- .finally(() => {
181
- // Keep the guard through the MutationObserver microtask generated by
182
- // restoring managed projections, preventing a verification loop.
142
+ void reconcileClipboardHandoff(doc).catch(() => undefined).finally(() => {
183
143
  setTimeout(() => { checking = false; }, 0);
184
144
  });
185
- }, RECONCILE_DEBOUNCE_MS);
186
- };
187
- const onCanonicalChange = () => {
188
- pruneCheckpoint();
189
- };
190
- const onVisibilityChange = () => {
191
- if (doc.visibilityState === "visible")
192
- schedule();
145
+ }, 450);
193
146
  };
194
- // Initializes the document observer before registering the listener.
147
+ const visible = () => { if (doc.visibilityState === "visible")
148
+ schedule(); };
195
149
  documentRevisions(doc);
196
- const unsubscribeRevision = subscribeDocumentRevision(doc, schedule);
197
- const unsubscribeChanges = subscribeChanges(onCanonicalChange);
198
- ownerWindow?.addEventListener("focus", schedule);
199
- ownerWindow?.addEventListener("pageshow", schedule);
200
- doc.addEventListener("visibilitychange", onVisibilityChange);
150
+ const stopRevision = subscribeDocumentRevision(doc, schedule);
151
+ const stopChanges = subscribeActiveDraftChanges(pruneCheckpoints);
152
+ view?.addEventListener("focus", schedule);
153
+ view?.addEventListener("pageshow", schedule);
154
+ doc.addEventListener("visibilitychange", visible);
201
155
  schedule();
202
156
  return () => {
203
157
  stopped = true;
204
158
  if (timer !== null)
205
159
  clearTimeout(timer);
206
- unsubscribeRevision();
207
- unsubscribeChanges();
208
- ownerWindow?.removeEventListener("focus", schedule);
209
- ownerWindow?.removeEventListener("pageshow", schedule);
210
- doc.removeEventListener("visibilitychange", onVisibilityChange);
160
+ stopRevision();
161
+ stopChanges();
162
+ view?.removeEventListener("focus", schedule);
163
+ view?.removeEventListener("pageshow", schedule);
164
+ doc.removeEventListener("visibilitychange", visible);
211
165
  };
212
166
  }
@@ -1,8 +1,11 @@
1
1
  import type { ChangeRecord } from "../changes/changesLog.js";
2
2
  import type { StructuralChange } from "../projection/structuralProjection.js";
3
3
  import type { AgentSketchMetadata } from "../agent/protocol.js";
4
+ import { type ElementComment } from "../comments/store.js";
4
5
  export interface FrameworkHints {
5
6
  framework?: string;
6
7
  stylingSystem?: string;
7
8
  }
8
- export declare function generatePrompt(changes: ChangeRecord[], frameworkHints?: FrameworkHints, structuralChanges?: readonly StructuralChange[], customInstructions?: string, sketches?: readonly AgentSketchMetadata[]): string;
9
+ /** Counts the final edit intents exported by the prompt, preserving gesture history for undo. */
10
+ export declare function countPromptChanges(changes: readonly ChangeRecord[], structuralChanges?: readonly StructuralChange[], sketchCount?: number): number;
11
+ export declare function generatePrompt(changes: ChangeRecord[], frameworkHints?: FrameworkHints, structuralChanges?: readonly StructuralChange[], customInstructions?: string, sketches?: readonly AgentSketchMetadata[], comments?: readonly ElementComment[]): string;