nudge-ui 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/dist/cli/agentCommand.js +4 -28
  2. package/dist/client.mjs +863 -213
  3. package/dist/hosts/next/wrapper.js +1 -7
  4. package/dist/hosts/projectBridge.js +3 -17
  5. package/dist/inspector/agent/McpConnectionDialog.js +7 -3
  6. package/dist/inspector/agent/activity.d.ts +1 -1
  7. package/dist/inspector/agent/activity.js +3 -2
  8. package/dist/inspector/agent/client.d.ts +2 -2
  9. package/dist/inspector/agent/client.js +28 -29
  10. package/dist/inspector/agent/connectionStatus.d.ts +3 -1
  11. package/dist/inspector/agent/connectionStatus.js +25 -0
  12. package/dist/inspector/agent/httpTransport.js +31 -147
  13. package/dist/inspector/agent/protocol.d.ts +1 -1
  14. package/dist/inspector/agent/protocol.js +1 -1
  15. package/dist/inspector/agent/verification.d.ts +1 -1
  16. package/dist/inspector/agent/verification.js +7 -4
  17. package/dist/inspector/boot.d.ts +3 -0
  18. package/dist/inspector/boot.js +297 -0
  19. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +2 -1
  20. package/dist/inspector/canvas/AgentActivityOverlay.js +6 -3
  21. package/dist/inspector/canvas/CanvasCard.js +21 -12
  22. package/dist/inspector/canvas/CanvasElementOverlay.js +82 -127
  23. package/dist/inspector/canvas/CanvasToolbar.js +2 -1
  24. package/dist/inspector/canvas/CanvasWorkspace.js +74 -97
  25. package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +3 -2
  26. package/dist/inspector/canvas/LockedWorkspaceNotice.js +10 -6
  27. package/dist/inspector/canvas/PageCard.d.ts +10 -0
  28. package/dist/inspector/canvas/PageCard.js +51 -0
  29. package/dist/inspector/canvas/SketchToolOptions.d.ts +5 -0
  30. package/dist/inspector/canvas/SketchToolOptions.js +86 -0
  31. package/dist/inspector/canvas/StaleChangeIndicator.d.ts +1 -1
  32. package/dist/inspector/canvas/StaleChangeIndicator.js +1 -1
  33. package/dist/inspector/canvas/ToolOptionsDrawer.d.ts +21 -0
  34. package/dist/inspector/canvas/ToolOptionsDrawer.js +77 -0
  35. package/dist/inspector/canvas/activeCanvasDocument.d.ts +3 -2
  36. package/dist/inspector/canvas/assets/eraser.svg +28 -0
  37. package/dist/inspector/canvas/assets/pen.svg +25 -0
  38. package/dist/inspector/canvas/canvasFrame.d.ts +16 -0
  39. package/dist/inspector/canvas/canvasFrame.js +10 -0
  40. package/dist/inspector/canvas/canvasGestures.d.ts +0 -1
  41. package/dist/inspector/canvas/canvasGestures.js +0 -6
  42. package/dist/inspector/canvas/canvasStore.d.ts +6 -0
  43. package/dist/inspector/canvas/canvasStore.js +36 -1
  44. package/dist/inspector/canvas/frameConnection.d.ts +48 -0
  45. package/dist/inspector/canvas/frameConnection.js +73 -0
  46. package/dist/inspector/canvas/frameProtocol.d.ts +18 -1
  47. package/dist/inspector/canvas/frameProtocol.js +36 -0
  48. package/dist/inspector/canvas/pageSurface.d.ts +11 -0
  49. package/dist/inspector/canvas/pageSurface.js +34 -0
  50. package/dist/inspector/canvas/projection.d.ts +19 -9
  51. package/dist/inspector/canvas/projection.js +58 -31
  52. package/dist/inspector/canvas/rendererBootstrap.js +4 -1
  53. package/dist/inspector/canvas/rendererElementSelector.js +9 -1
  54. package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
  55. package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
  56. package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
  57. package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
  58. package/dist/inspector/canvas/rendererStylesheet.js +15 -0
  59. package/dist/inspector/canvas/sessionStore.d.ts +2 -2
  60. package/dist/inspector/canvas/sessionStore.js +12 -9
  61. package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
  62. package/dist/inspector/canvas/staleChangeDetector.js +10 -22
  63. package/dist/inspector/changes/codecs.d.ts +1 -0
  64. package/dist/inspector/changes/codecs.js +20 -5
  65. package/dist/inspector/changes/draftPreview.d.ts +12 -0
  66. package/dist/inspector/changes/draftPreview.js +133 -0
  67. package/dist/inspector/changes/editModel.d.ts +8 -0
  68. package/dist/inspector/changes/editModel.js +8 -2
  69. package/dist/inspector/changes/managedStyleProjection.js +2 -1
  70. package/dist/inspector/changes/previewDiagnostics.js +1 -1
  71. package/dist/inspector/changes/types.d.ts +11 -1
  72. package/dist/inspector/comments/CommentsOverlay.js +18 -16
  73. package/dist/inspector/comments/store.js +3 -2
  74. package/dist/inspector/comments/verification.js +1 -1
  75. package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
  76. package/dist/inspector/componentSemantics/changeAction.js +1 -1
  77. package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
  78. package/dist/inspector/componentSemantics/textBinding.js +4 -45
  79. package/dist/inspector/drafts/draftStore.d.ts +109 -0
  80. package/dist/inspector/drafts/draftStore.js +451 -0
  81. package/dist/inspector/drafts/model.d.ts +9 -1
  82. package/dist/inspector/drafts/model.js +5 -1
  83. package/dist/inspector/drafts/store.d.ts +53 -15
  84. package/dist/inspector/drafts/store.js +143 -169
  85. package/dist/inspector/index.d.ts +2 -4
  86. package/dist/inspector/index.js +2 -264
  87. package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
  88. package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
  89. package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
  90. package/dist/inspector/inspection/bridge.d.ts +1 -1
  91. package/dist/inspector/inspection/bridge.js +2 -1
  92. package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
  93. package/dist/inspector/inspection/browserCssInspection.js +28 -6
  94. package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
  95. package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
  96. package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
  97. package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
  98. package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
  99. package/dist/inspector/iterations/capture.d.ts +2 -1
  100. package/dist/inspector/iterations/client.d.ts +2 -1
  101. package/dist/inspector/iterations/coordinator.d.ts +4 -3
  102. package/dist/inspector/iterations/coordinator.js +5 -3
  103. package/dist/inspector/iterations/prompt.js +9 -8
  104. package/dist/inspector/overlay/interactionStyles.js +17 -0
  105. package/dist/inspector/pageInspector.d.ts +42 -0
  106. package/dist/inspector/pageInspector.js +342 -0
  107. package/dist/inspector/projection/draftProjection.d.ts +10 -2
  108. package/dist/inspector/projection/draftProjection.js +9 -1
  109. package/dist/inspector/projection/managedStylesheet.js +24 -4
  110. package/dist/inspector/projection/renderedInstance.js +7 -1
  111. package/dist/inspector/projection/structuralProjection.d.ts +0 -9
  112. package/dist/inspector/projection/structuralProjection.js +14 -44
  113. package/dist/inspector/projection/textProjection.d.ts +8 -2
  114. package/dist/inspector/projection/textProjection.js +21 -13
  115. package/dist/inspector/prompt/clipboardHandoff.d.ts +9 -0
  116. package/dist/inspector/prompt/clipboardHandoff.js +42 -16
  117. package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
  118. package/dist/inspector/prompt/generatePrompt.js +243 -147
  119. package/dist/inspector/prompt/promptDocument.d.ts +3 -0
  120. package/dist/inspector/prompt/promptDocument.js +8 -0
  121. package/dist/inspector/prompt/promptSettings.js +7 -5
  122. package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
  123. package/dist/inspector/runtime/inspectorStorage.js +42 -0
  124. package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
  125. package/dist/inspector/runtime/ownedStylesheets.js +72 -0
  126. package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
  127. package/dist/inspector/runtime/runtimeConfig.js +10 -1
  128. package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
  129. package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
  130. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
  131. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
  132. package/dist/inspector/selection/editScope.d.ts +1 -0
  133. package/dist/inspector/selection/editScope.js +13 -2
  134. package/dist/inspector/shell/AgentStatusButton.js +2 -2
  135. package/dist/inspector/shell/AppShell.js +6 -2
  136. package/dist/inspector/shell/ChangesLog.js +27 -9
  137. package/dist/inspector/shell/CopyPromptButton.js +60 -54
  138. package/dist/inspector/shell/EmptyState.js +3 -3
  139. package/dist/inspector/shell/InspectorShell.d.ts +0 -3
  140. package/dist/inspector/shell/InspectorShell.js +21 -13
  141. package/dist/inspector/shell/PeekOriginalButton.js +3 -3
  142. package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
  143. package/dist/inspector/shell/interactionStatePreview.js +112 -0
  144. package/dist/inspector/shell/originalPreview.d.ts +3 -2
  145. package/dist/inspector/shell/originalPreview.js +10 -37
  146. package/dist/inspector/shell/stateValue.js +2 -3
  147. package/dist/inspector/sketch/SketchEditor.js +5 -4
  148. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
  149. package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
  150. package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
  151. package/dist/inspector/sketch/SketchOverlay.js +139 -263
  152. package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
  153. package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
  154. package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
  155. package/dist/inspector/sketch/SketchThumbnail.js +18 -0
  156. package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
  157. package/dist/inspector/sketch/SketchWorkspace.js +69 -34
  158. package/dist/inspector/sketch/capture.js +19 -3
  159. package/dist/inspector/sketch/copySketch.d.ts +10 -0
  160. package/dist/inspector/sketch/copySketch.js +52 -0
  161. package/dist/inspector/sketch/drawingTools.d.ts +17 -0
  162. package/dist/inspector/sketch/drawingTools.js +47 -0
  163. package/dist/inspector/sketch/frameHeading.d.ts +9 -0
  164. package/dist/inspector/sketch/frameHeading.js +30 -0
  165. package/dist/inspector/sketch/freehand.js +3 -3
  166. package/dist/inspector/sketch/geometry.d.ts +6 -0
  167. package/dist/inspector/sketch/geometry.js +19 -0
  168. package/dist/inspector/sketch/history.d.ts +30 -0
  169. package/dist/inspector/sketch/history.js +58 -0
  170. package/dist/inspector/sketch/interaction.d.ts +1 -3
  171. package/dist/inspector/sketch/interaction.js +6 -9
  172. package/dist/inspector/sketch/model.d.ts +35 -1
  173. package/dist/inspector/sketch/model.js +116 -61
  174. package/dist/inspector/sketch/persistence.d.ts +14 -0
  175. package/dist/inspector/sketch/persistence.js +63 -7
  176. package/dist/inspector/sketch/raster.d.ts +6 -2
  177. package/dist/inspector/sketch/raster.js +43 -17
  178. package/dist/inspector/sketch/store.d.ts +1 -1
  179. package/dist/inspector/sketch/store.js +1 -1
  180. package/dist/inspector/sketch/videoFrames.d.ts +6 -0
  181. package/dist/inspector/sketch/videoFrames.js +81 -0
  182. package/dist/inspector/styleEditors/BackgroundEditor.js +2 -1
  183. package/dist/inspector/styleEditors/BorderEditor.js +30 -130
  184. package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
  185. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
  186. package/dist/inspector/styleEditors/LayoutSection.js +2 -2
  187. package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
  188. package/dist/inspector/styleEditors/borderModel.js +114 -0
  189. package/dist/inspector/styleEditors/layoutValue.js +2 -3
  190. package/dist/inspector/tokens/TokenField.js +34 -3
  191. package/dist/inspector/tokens/TokensPanel.js +2 -1
  192. package/dist/inspector/tokens/editActions.d.ts +3 -3
  193. package/dist/inspector/tokens/editActions.js +9 -3
  194. package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
  195. package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
  196. package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
  197. package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
  198. package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
  199. package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
  200. package/dist/inspector/tokens/resolution.d.ts +6 -3
  201. package/dist/inspector/tokens/resolution.js +91 -112
  202. package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
  203. package/dist/inspector/tokens/tokenEdits.js +1 -1
  204. package/dist/inspector/ui/InlineStyleWarning.js +2 -2
  205. package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
  206. package/dist/inspector/ui/InspectorStyle.js +26 -0
  207. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  208. package/dist/inspector/ui/PopoverListbox.js +3 -3
  209. package/dist/inspector/ui/Toast.d.ts +14 -0
  210. package/dist/inspector/ui/Toast.js +30 -0
  211. package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
  212. package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
  213. package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
  214. package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
  215. package/dist/inspector/ui/customCursors.d.ts +5 -0
  216. package/dist/inspector/ui/customCursors.js +32 -6
  217. package/dist/inspector/ui/styles.js +12 -10
  218. package/dist/inspector/workspace/commands.js +1 -2
  219. package/dist/inspector/workspace/controller.js +1 -2
  220. package/dist/inspector/workspace/handoff.js +3 -2
  221. package/dist/inspector/workspace/sentItems.d.ts +3 -6
  222. package/dist/inspector/workspace/sentItems.js +6 -24
  223. package/dist/inspector/workspace/timeline.d.ts +24 -10
  224. package/dist/inspector/workspace/timeline.js +118 -96
  225. package/dist/internal/agent-protocol.d.ts +50 -13
  226. package/dist/internal/agent-protocol.js +145 -19
  227. package/dist/nudge-ui.mjs +81 -65
  228. package/dist/project/mcpPackage.d.ts +8 -0
  229. package/dist/project/mcpPackage.js +60 -0
  230. package/package.json +12 -2
  231. package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
  232. package/dist/inspector/canvas/measurementProjection.js +0 -16
  233. package/dist/inspector/changes/changesLog.d.ts +0 -49
  234. package/dist/inspector/changes/changesLog.js +0 -255
  235. package/dist/inspector/changes/draftChanges.d.ts +0 -58
  236. package/dist/inspector/changes/draftChanges.js +0 -304
  237. package/dist/inspector/conformance/projection.d.ts +0 -2
  238. package/dist/inspector/conformance/projection.js +0 -3
  239. package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
  240. package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
  241. package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
  242. package/dist/inspector/overlay/InspectorOverlay.js +0 -332
  243. package/dist/inspector/overlay/elementSelector.d.ts +0 -1
  244. package/dist/inspector/overlay/elementSelector.js +0 -111
  245. package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
  246. package/dist/inspector/sketch/SketchChanges.js +0 -72
  247. package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
  248. package/dist/inspector/sketch/SketchEntryButton.js +0 -11
  249. package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
  250. package/dist/inspector/sketch/SketchLayersPanel.js +0 -71
@@ -1,13 +1,11 @@
1
- import { draftIdForCard } from "../drafts/store.js";
2
- import { isPreviewableChange, isTokenChange, } from "../changes/changesLog.js";
1
+ import { draftIdForCard, getActiveDraftChanges } from "../drafts/store.js";
2
+ import { isPreviewableChange, isTokenChange, } from "../changes/types.js";
3
3
  import { changeKey } from "../changes/model.js";
4
4
  import { beginPreviewAttempt, clearPreviewDiagnostics, getHostPreviewDocument, notifyPreviewDiagnostics, publishPreviewDiagnostic, } from "../changes/previewDiagnostics.js";
5
- import { getActiveDraftChanges } from "../changes/draftChanges.js";
6
5
  import { getCanvasPreviewDocument, getRegisteredFrames } from "./projection.js";
7
- import { findCanvasFrameBySource, PROJECT_ID, WORKSPACE_ID } from "./projection.js";
6
+ import { subscribeCanvasRendererMessages } from "./rendererMessageRouter.js";
8
7
  import { getCanvasCards } from "./canvasStore.js";
9
8
  import { getCanvasMode } from "./viewStore.js";
10
- import { isRendererMessageFor } from "./frameProtocol.js";
11
9
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
12
10
  const VERIFICATION_TIMEOUT_MS = 5000;
13
11
  const STALE_CHECK_DEBOUNCE_MS = 100;
@@ -15,6 +13,7 @@ let verificationTimer = null;
15
13
  let debounceTimer = null;
16
14
  let pendingChanges = null;
17
15
  let pendingAttempts = new Map();
16
+ let stopFrameReady = null;
18
17
  function checkSelectorInDocument(doc, selector) {
19
18
  try {
20
19
  return doc.querySelectorAll(selector).length > 0;
@@ -189,22 +188,10 @@ export function startStaleDetection(changes) {
189
188
  verificationTimer = null;
190
189
  notifyPreviewDiagnostics();
191
190
  }, VERIFICATION_TIMEOUT_MS);
192
- window.addEventListener("message", handleFrameReady);
193
- }
194
- function handleFrameReady(event) {
195
- if (event.origin !== window.location.origin)
196
- return;
197
- const msg = event.data;
198
- const frame = findCanvasFrameBySource(event.source);
199
- if (!frame || !isRendererMessageFor(msg, {
200
- projectId: PROJECT_ID,
201
- workspaceId: WORKSPACE_ID,
202
- cardId: frame.cardId,
203
- }) || msg.type !== "frame-ready")
204
- return;
205
- if (!pendingChanges)
206
- return;
207
- scheduleStaleCheck();
191
+ stopFrameReady ??= subscribeCanvasRendererMessages(({ message }) => {
192
+ if (message.type === "frame-ready" && pendingChanges)
193
+ scheduleStaleCheck();
194
+ });
208
195
  }
209
196
  export function cancelStaleDetection() {
210
197
  if (verificationTimer) {
@@ -217,7 +204,8 @@ export function cancelStaleDetection() {
217
204
  }
218
205
  pendingChanges = null;
219
206
  pendingAttempts = new Map();
220
- window.removeEventListener("message", handleFrameReady);
207
+ stopFrameReady?.();
208
+ stopFrameReady = null;
221
209
  notifyPreviewDiagnostics();
222
210
  }
223
211
  export function isVerificationPending() {
@@ -34,6 +34,7 @@ export interface SerializableElementChange {
34
34
  runtimeEvidence?: NonNullable<ElementChangeRecord["runtimeEvidence"]>;
35
35
  sourceEvidence?: NonNullable<ElementChangeRecord["sourceEvidence"]>;
36
36
  scope?: EditScope;
37
+ sourceInstanceCount?: number;
37
38
  instanceOverride?: RenderedInstanceOverride;
38
39
  state?: NonNullable<ElementChangeRecord["state"]>;
39
40
  }
@@ -16,7 +16,7 @@ export function isSerializableElementChangeValue(value) {
16
16
  || !hasOnlyKeys(value, [
17
17
  "kind", "cid", "file", "line", "column", "selector", "property",
18
18
  "sourceProperty", "sourceAuthoredValue", "oldToken", "newToken", "rawValue",
19
- "oldRawValue", "source", "runtimeEvidence", "sourceEvidence", "scope", "instanceOverride", "state",
19
+ "oldRawValue", "source", "runtimeEvidence", "sourceEvidence", "scope", "sourceInstanceCount", "instanceOverride", "state",
20
20
  ])
21
21
  || (value.kind !== undefined && value.kind !== "element")
22
22
  || typeof value.cid !== "string"
@@ -35,6 +35,7 @@ export function isSerializableElementChangeValue(value) {
35
35
  || (value.runtimeEvidence !== undefined && !isRuntimeElementEvidenceValue(value.runtimeEvidence))
36
36
  || (value.sourceEvidence !== undefined && !isSourceElementEvidenceValue(value.sourceEvidence))
37
37
  || !isEditScopeValue(value.scope)
38
+ || (value.sourceInstanceCount !== undefined && (!Number.isSafeInteger(value.sourceInstanceCount) || value.sourceInstanceCount < 1))
38
39
  || (value.instanceOverride !== undefined && value.scope !== "rendered-instance")
39
40
  || (value.scope === "rendered-instance" && !isRenderedInstanceOverride(value.instanceOverride))
40
41
  || (value.state !== undefined && !isElementStateValue(value.state))) {
@@ -118,6 +119,7 @@ export function serializeElementChange(change) {
118
119
  runtimeEvidence: change.runtimeEvidence ? { ...change.runtimeEvidence } : undefined,
119
120
  sourceEvidence: change.sourceEvidence ? { ...change.sourceEvidence } : undefined,
120
121
  scope: change.scope === "rendered-instance" ? "rendered-instance" : "source-site",
122
+ ...(change.sourceInstanceCount !== undefined ? { sourceInstanceCount: change.sourceInstanceCount } : {}),
121
123
  instanceOverride: change.scope === "rendered-instance" ? change.instanceOverride : undefined,
122
124
  state: change.state,
123
125
  };
@@ -163,6 +165,9 @@ export function serializeTextContentChange(change) {
163
165
  props: change.target.props,
164
166
  ariaLabel: change.target.ariaLabel,
165
167
  beforeText: change.target.beforeText,
168
+ ...(change.target.tagName !== undefined ? { tagName: change.target.tagName } : {}),
169
+ ...(change.target.elementId !== undefined ? { elementId: change.target.elementId } : {}),
170
+ ...(change.target.domPath !== undefined ? { domPath: change.target.domPath } : {}),
166
171
  ...(change.target.textNodePath?.length
167
172
  ? { textNodePath: [...change.target.textNodePath] }
168
173
  : {}),
@@ -204,6 +209,7 @@ export function deserializeElementChange(serialized) {
204
209
  runtimeEvidence: serialized.runtimeEvidence ? { ...serialized.runtimeEvidence } : undefined,
205
210
  sourceEvidence: serialized.sourceEvidence ? { ...serialized.sourceEvidence } : undefined,
206
211
  scope: serialized.scope ?? "source-site",
212
+ ...(serialized.sourceInstanceCount !== undefined ? { sourceInstanceCount: serialized.sourceInstanceCount } : {}),
207
213
  instanceOverride: serialized.scope === "rendered-instance" ? serialized.instanceOverride : undefined,
208
214
  state: serialized.state,
209
215
  };
@@ -248,6 +254,9 @@ export function deserializeTextContentChange(serialized) {
248
254
  props: serialized.target.props,
249
255
  ariaLabel: serialized.target.ariaLabel,
250
256
  beforeText: serialized.target.beforeText,
257
+ ...(serialized.target.tagName !== undefined ? { tagName: serialized.target.tagName } : {}),
258
+ ...(serialized.target.elementId !== undefined ? { elementId: serialized.target.elementId } : {}),
259
+ ...(serialized.target.domPath !== undefined ? { domPath: serialized.target.domPath } : {}),
251
260
  ...(serialized.target.textNodePath?.length
252
261
  ? { textNodePath: [...serialized.target.textNodePath] }
253
262
  : {}),
@@ -327,18 +336,24 @@ function isComponentInvocationEvidenceValue(value) {
327
336
  }
328
337
  function isRuntimeElementEvidenceValue(value) {
329
338
  return isRecord(value)
330
- && hasOnlyKeys(value, ["reason", "tagName", "text", "props", "ariaLabel"])
339
+ && hasOnlyKeys(value, ["reason", "tagName", "text", "props", "ariaLabel", "domPath", "elementId"])
331
340
  && (value.reason === undefined || value.reason === "runtime-created" || value.reason === "unannotated")
332
341
  && typeof value.tagName === "string"
333
342
  && (typeof value.text === "string" || value.text === null)
334
343
  && (typeof value.props === "string" || value.props === null)
335
- && (typeof value.ariaLabel === "string" || value.ariaLabel === null);
344
+ && (typeof value.ariaLabel === "string" || value.ariaLabel === null)
345
+ && (value.domPath === undefined || typeof value.domPath === "string" || value.domPath === null)
346
+ && (value.elementId === undefined || typeof value.elementId === "string" || value.elementId === null);
336
347
  }
337
348
  function isSourceElementEvidenceValue(value) {
338
349
  return isRecord(value)
339
- && hasOnlyKeys(value, ["tagName", "props"])
350
+ && hasOnlyKeys(value, ["tagName", "props", "text", "ariaLabel", "domPath", "elementId"])
340
351
  && typeof value.tagName === "string"
341
- && (typeof value.props === "string" || value.props === null);
352
+ && (typeof value.props === "string" || value.props === null)
353
+ && (value.text === undefined || typeof value.text === "string" || value.text === null)
354
+ && (value.ariaLabel === undefined || typeof value.ariaLabel === "string" || value.ariaLabel === null)
355
+ && (value.domPath === undefined || typeof value.domPath === "string" || value.domPath === null)
356
+ && (value.elementId === undefined || typeof value.elementId === "string" || value.elementId === null);
342
357
  }
343
358
  function isTokenRefValue(value) {
344
359
  if (value === null)
@@ -0,0 +1,12 @@
1
+ import type { DraftChangesSnapshot, DraftProjectionEvent } from "../drafts/draftStore.js";
2
+ /** The host document's preview of the active draft. */
3
+ export interface DraftPreview {
4
+ project(snapshot: DraftChangesSnapshot, event: DraftProjectionEvent): void;
5
+ dispose(): void;
6
+ }
7
+ /**
8
+ * Projects the active draft into the host document and verifies the managed
9
+ * CSS off the commit path (0044). Verification re-checks the full surviving
10
+ * set: a new rule can change cascade or specificity for earlier changes.
11
+ */
12
+ export declare function createDraftPreview(readActive: () => DraftChangesSnapshot): DraftPreview;
@@ -0,0 +1,133 @@
1
+ import { getSelectedElement } from "../selection/selectionStore.js";
2
+ import { changeKey } from "./model.js";
3
+ import { verifyManagedStyleProjection } from "./managedStyleProjection.js";
4
+ import { isPreviewableChange } from "./types.js";
5
+ import { beginPreviewAttempt, clearPreviewDiagnostics, getHostPreviewDocument, publishPreviewDiagnostic, } from "./previewDiagnostics.js";
6
+ import { clearStructuralProjectionReports, pruneStructuralProjectionReports } from "../projection/structuralProjection.js";
7
+ import { applyHostDraftProjection, compileDraftProjection } from "../projection/draftProjection.js";
8
+ import { isEditorShellDocument } from "../runtime/editorShell.js";
9
+ import { isOriginalPreviewActive, subscribeOriginalPreview } from "../shell/originalPreview.js";
10
+ /**
11
+ * Projects the active draft into the host document and verifies the managed
12
+ * CSS off the commit path (0044). Verification re-checks the full surviving
13
+ * set: a new rule can change cascade or specificity for earlier changes.
14
+ */
15
+ export function createDraftPreview(readActive) {
16
+ let pendingTargets = new Map();
17
+ let handle = null;
18
+ function reapply(snapshot) {
19
+ // While hold-to-view-original is active the host document intentionally
20
+ // shows the page without inspector changes. Releasing re-projects.
21
+ if (isOriginalPreviewActive())
22
+ return;
23
+ if (!isEditorShellDocument())
24
+ applyHostDraftProjection(compileDraftProjection(snapshot));
25
+ }
26
+ function flushVerification() {
27
+ handle = null;
28
+ // Probing the original preview would report the held-back changes as
29
+ // conflicts. Keep the pending targets; releasing re-verifies them.
30
+ if (isOriginalPreviewActive())
31
+ return;
32
+ const targets = pendingTargets;
33
+ pendingTargets = new Map();
34
+ // The editor shell has no application document. Renderer diagnostics are
35
+ // owned by renderer projection reports.
36
+ if (targets.size === 0 || isEditorShellDocument())
37
+ return;
38
+ const current = readActive();
39
+ const attempt = beginPreviewAttempt(getHostPreviewDocument(), current.revision);
40
+ if (!attempt)
41
+ return;
42
+ for (const change of current.changes) {
43
+ if (!isPreviewableChange(change))
44
+ continue;
45
+ const key = changeKey(change);
46
+ if (!targets.has(key))
47
+ continue;
48
+ const result = verifyManagedStyleProjection(change, targets.get(key) ?? null);
49
+ if (result)
50
+ publishPreviewDiagnostic(attempt, key, result);
51
+ }
52
+ }
53
+ function scheduleVerification() {
54
+ if (handle !== null)
55
+ return;
56
+ const view = typeof window !== "undefined" ? window : undefined;
57
+ if (view && typeof view.requestIdleCallback === "function") {
58
+ handle = view.requestIdleCallback(flushVerification, { timeout: 200 });
59
+ }
60
+ else if (view && typeof view.requestAnimationFrame === "function") {
61
+ handle = view.requestAnimationFrame(flushVerification);
62
+ }
63
+ else {
64
+ handle = setTimeout(flushVerification, 0);
65
+ }
66
+ }
67
+ function markForVerification(snapshot, verificationTargets) {
68
+ const selectedElement = getSelectedElement()?.domElement ?? null;
69
+ let added = false;
70
+ for (const key of snapshot.changes.map(changeKey)) {
71
+ if (!pendingTargets.has(key))
72
+ added = true;
73
+ // Keep the latest commit context for a merged key. The element may be in
74
+ // an iframe, so this is also the document for verification.
75
+ pendingTargets.set(key, verificationTargets?.get(key) ?? selectedElement);
76
+ }
77
+ if (added)
78
+ scheduleVerification();
79
+ }
80
+ const stopOriginalPreview = subscribeOriginalPreview(() => {
81
+ if (isEditorShellDocument())
82
+ return;
83
+ const snapshot = readActive();
84
+ if (isOriginalPreviewActive()) {
85
+ applyHostDraftProjection({
86
+ sourceRevision: snapshot.revision,
87
+ managedStyles: { rules: [], css: "" },
88
+ instanceOverrides: [],
89
+ structuralChanges: [],
90
+ textContentChanges: [],
91
+ componentOverrides: [],
92
+ });
93
+ return;
94
+ }
95
+ reapply(snapshot);
96
+ // A flush during the hold kept its targets without scheduling, so marking
97
+ // alone would find every key present and schedule nothing.
98
+ markForVerification(snapshot);
99
+ if (pendingTargets.size > 0)
100
+ scheduleVerification();
101
+ });
102
+ return {
103
+ project(snapshot, event) {
104
+ switch (event.kind) {
105
+ case "edit":
106
+ reapply(snapshot);
107
+ markForVerification(snapshot, event.verificationTargets);
108
+ return;
109
+ case "reconcile":
110
+ reapply(snapshot);
111
+ pruneStructuralProjectionReports(event.structuralIds);
112
+ markForVerification(snapshot);
113
+ return;
114
+ case "switch":
115
+ pendingTargets.clear();
116
+ reapply(snapshot);
117
+ markForVerification(snapshot);
118
+ return;
119
+ case "reset":
120
+ // A deferred verification must not inspect a new record with the
121
+ // selection context of the cleared one.
122
+ pendingTargets.clear();
123
+ clearPreviewDiagnostics();
124
+ clearStructuralProjectionReports();
125
+ reapply(snapshot);
126
+ }
127
+ },
128
+ dispose() {
129
+ stopOriginalPreview();
130
+ pendingTargets.clear();
131
+ },
132
+ };
133
+ }
@@ -18,6 +18,10 @@ export interface RenderedInstanceRef {
18
18
  text: string | null;
19
19
  /** An accessible name can distinguish icon-only repeated controls. */
20
20
  ariaLabel?: string | null;
21
+ /** Rendered facts used to reattach generated identities after reload. */
22
+ tagName?: string;
23
+ domPath?: string | null;
24
+ elementId?: string | null;
21
25
  /**
22
26
  * The component invocation whose entire output is this element. Present
23
27
  * only when evidence alone cannot tell identical outputs apart.
@@ -50,6 +54,10 @@ export interface TextProjectionTarget {
50
54
  props: string | null;
51
55
  ariaLabel: string | null;
52
56
  beforeText: string;
57
+ /** Optional prompt details; never used to resolve or group text targets. */
58
+ tagName?: string;
59
+ elementId?: string | null;
60
+ domPath?: string | null;
53
61
  /** Document-local evidence for a text node inside safe mixed markup. */
54
62
  textNodePath?: readonly number[];
55
63
  }
@@ -14,13 +14,16 @@ export function isRenderedInstanceRef(value) {
14
14
  || typeof source.cid !== "string"
15
15
  || typeof source.src !== "string"
16
16
  || !isRecord(locator)
17
- || !hasOnlyKeys(locator, ["kind", "occurrence", "props", "text", "ariaLabel", "invocation"]))
17
+ || !hasOnlyKeys(locator, ["kind", "occurrence", "props", "text", "ariaLabel", "tagName", "domPath", "elementId", "invocation"]))
18
18
  return false;
19
19
  return locator.kind === "evidence"
20
20
  && Number.isSafeInteger(locator.occurrence) && locator.occurrence >= 0
21
21
  && (typeof locator.props === "string" || locator.props === null)
22
22
  && (typeof locator.text === "string" || locator.text === null)
23
23
  && (locator.ariaLabel === undefined || typeof locator.ariaLabel === "string" || locator.ariaLabel === null)
24
+ && (locator.tagName === undefined || typeof locator.tagName === "string")
25
+ && (locator.domPath === undefined || typeof locator.domPath === "string" || locator.domPath === null)
26
+ && (locator.elementId === undefined || typeof locator.elementId === "string" || locator.elementId === null)
24
27
  && (locator.invocation === undefined || isInvocationRef(locator.invocation));
25
28
  }
26
29
  export function isRenderedInstanceOverride(value) {
@@ -46,7 +49,7 @@ export function isStyleRuleContextValue(value) {
46
49
  }
47
50
  export function isTextProjectionTargetValue(value) {
48
51
  if (!isRecord(value)
49
- || !hasOnlyKeys(value, ["sourceSite", "occurrence", "props", "ariaLabel", "beforeText", "textNodePath"])) {
52
+ || !hasOnlyKeys(value, ["sourceSite", "occurrence", "props", "ariaLabel", "beforeText", "textNodePath", "tagName", "elementId", "domPath"])) {
50
53
  return false;
51
54
  }
52
55
  const occurrence = value.occurrence;
@@ -58,6 +61,9 @@ export function isTextProjectionTargetValue(value) {
58
61
  || (typeof props !== "string" && props !== null)
59
62
  || (typeof ariaLabel !== "string" && ariaLabel !== null)
60
63
  || typeof beforeText !== "string"
64
+ || (value.tagName !== undefined && typeof value.tagName !== "string")
65
+ || (value.elementId !== undefined && value.elementId !== null && typeof value.elementId !== "string")
66
+ || (value.domPath !== undefined && value.domPath !== null && typeof value.domPath !== "string")
61
67
  || (textNodePath !== undefined
62
68
  && (!Array.isArray(textNodePath)
63
69
  || textNodePath.length === 0
@@ -2,6 +2,7 @@ import { verifyPreview } from "../projection/managedStylesheet.js";
2
2
  import { getSelectedElement } from "../selection/selectionStore.js";
3
3
  import { instanceSelector } from "../projection/renderedInstance.js";
4
4
  import { selectorForInteractionState } from "../shell/styleState.js";
5
+ import { selectorForInteractionStatePreview } from "../shell/interactionStatePreview.js";
5
6
  import { isComponentChange, isTextContentChange, isTokenChange, } from "./types.js";
6
7
  import { tokenReference } from "./model.js";
7
8
  function requestedStyleValue(change) {
@@ -81,7 +82,7 @@ export function verifyManagedStyleProjection(change, selectedElement, ownerDocum
81
82
  let targets = [];
82
83
  try {
83
84
  const selector = selectorForManagedChange(change);
84
- targets = selector ? Array.from(ownerDocument.querySelectorAll(selector)) : [];
85
+ targets = selector ? Array.from(ownerDocument.querySelectorAll(selectorForInteractionStatePreview(selector))) : [];
85
86
  }
86
87
  catch {
87
88
  targets = [];
@@ -1,4 +1,4 @@
1
- import { getActiveDraftChanges, getDraftChanges } from "./draftChanges.js";
1
+ import { getActiveDraftChanges, getDraftChanges } from "../drafts/store.js";
2
2
  const HOST_DOCUMENT = {
3
3
  logicalDocument: "host",
4
4
  sessionId: "host-document",
@@ -4,7 +4,7 @@ import type { EditScope, RenderedInstanceOverride, StyleRuleContext, TextContent
4
4
  /** Bounded rendered facts retained for one element without an authored source location. */
5
5
  export interface RuntimeElementEvidence {
6
6
  /**
7
- * Why the location is unknown; prompts phrase their guidance accordingly.
7
+ * Why the element has no authored source location.
8
8
  * Omitted by legacy records, which are all runtime-created DOM.
9
9
  */
10
10
  reason?: "runtime-created" | "unannotated";
@@ -12,11 +12,19 @@ export interface RuntimeElementEvidence {
12
12
  text: string | null;
13
13
  props: string | null;
14
14
  ariaLabel: string | null;
15
+ /** Nearest-ancestor selector path; omitted by records captured before it existed. */
16
+ domPath?: string | null;
17
+ elementId?: string | null;
15
18
  }
16
19
  /** Bounded authored facts for one annotated element. */
17
20
  export interface SourceElementEvidence {
18
21
  tagName: string;
19
22
  props: string | null;
23
+ /** Rendered identification details; omitted by older saved changes. */
24
+ text?: string | null;
25
+ ariaLabel?: string | null;
26
+ domPath?: string | null;
27
+ elementId?: string | null;
20
28
  }
21
29
  export interface ElementChangeRecord {
22
30
  kind?: "element";
@@ -45,6 +53,8 @@ export interface ElementChangeRecord {
45
53
  /** Authored facts that relocate the source site after its position shifts. */
46
54
  sourceEvidence?: SourceElementEvidence;
47
55
  scope?: EditScope;
56
+ /** Matching rendered items when captured; presentation only, absent in older edits. */
57
+ sourceInstanceCount?: number;
48
58
  /** Durable, controller-owned target for one rendered output. */
49
59
  instanceOverride?: RenderedInstanceOverride;
50
60
  state?: "base" | "hover" | "active" | "focus" | "focus-visible" | "disabled";
@@ -4,7 +4,9 @@ import { IconCheck, IconTrash } from "@tabler/icons-react";
4
4
  import { useCanvasMode } from "../canvas/viewStore.js";
5
5
  import { getRegisteredFrames } from "../canvas/projection.js";
6
6
  import { subscribeCanvasRendererMessages } from "../canvas/rendererMessageRouter.js";
7
- import { RENDERER_ELEMENT_ID_ATTR } from "../canvas/rendererCidIndex.js";
7
+ import { measureFrameViewport, toViewportPoint } from "../canvas/frameConnection.js";
8
+ import { canvasFrameElement } from "../canvas/canvasFrame.js";
9
+ import { getElementComputedStyle } from "../runtime/domRealm.js";
8
10
  import { captureRenderedInstance } from "../projection/renderedInstance.js";
9
11
  import { IconButton } from "../ui/IconButton.js";
10
12
  import { TextInput } from "../ui/TextInput.js";
@@ -12,21 +14,21 @@ import { commentFingerprint, commentViewport, resolveCommentElement } from "./el
12
14
  import { commentRoute, getCommentClearRevision, isCommentToolActive, removeComment, saveComment, subscribeComments, useComments, useCommentToolActive } from "./store.js";
13
15
  import { isReadingCommentSource, readCommentSource, reconcileComments } from "./verification.js";
14
16
  const styles = ".comment-pin {\n position: fixed;\n z-index: 10;\n display: grid;\n place-items: center;\n width: var(--icon-size-large);\n height: var(--icon-size-large);\n padding: 0;\n border: 2px solid var(--surface-raised-2x);\n border-radius: 50%;\n background: var(--accent);\n box-shadow: var(--shadow-popup);\n cursor: pointer;\n pointer-events: auto;\n}\n\n.comment-editor-backdrop {\n position: fixed;\n inset: 0;\n z-index: 11;\n pointer-events: auto;\n}\n\n.comment-editor {\n position: fixed;\n z-index: 12;\n display: flex;\n align-items: center;\n gap: var(--space-4);\n width: min(384px, calc(100vw - 16px));\n box-sizing: border-box;\n padding: var(--space-8);\n border-radius: var(--radius-12);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n pointer-events: auto;\n}\n\n.comment-editor .text-input { flex: 1; min-width: 0; padding-inline: var(--space-8); }\n.comment-editor__error { position: absolute; top: 100%; left: 0; padding: var(--space-8); background: var(--surface-raised-2x); color: var(--text-primary); border-radius: var(--radius-control); }\n";
17
+ import { InspectorStyle } from "../ui/InspectorStyle.js";
15
18
  function sameAnchor(left, right) {
16
19
  return left?.left === right?.left && left?.top === right?.top;
17
20
  }
18
21
  function anchorFor(iframe, element) {
19
22
  if (!element.isConnected || iframe.contentDocument !== element.ownerDocument)
20
23
  return null;
21
- // Focus view keeps other frames mounted, but their pins must stay hidden.
22
- if (getComputedStyle(iframe).visibility !== "visible")
24
+ // Focus view keeps real frames mounted, but their pins must stay hidden.
25
+ const frameElement = canvasFrameElement(iframe);
26
+ if (frameElement && getElementComputedStyle(frameElement).visibility !== "visible")
23
27
  return null;
24
- const frame = iframe.getBoundingClientRect();
28
+ const frame = measureFrameViewport(iframe);
25
29
  const rect = element.getBoundingClientRect();
26
- const scale = frame.width / (iframe.clientWidth || frame.width);
27
- const left = frame.left + rect.right * scale;
28
- const top = frame.top + rect.top * scale;
29
- if (top < frame.top || top > frame.bottom || left < frame.left || left > frame.right)
30
+ const { x: left, y: top } = toViewportPoint(frame, { x: rect.right, y: rect.top });
31
+ if (top < frame.top || top > frame.top + frame.height || left < frame.left || left > frame.left + frame.width)
30
32
  return null;
31
33
  return { left, top };
32
34
  }
@@ -75,17 +77,17 @@ export function CommentsOverlay() {
75
77
  window.addEventListener("keydown", dismissOnEscape, true);
76
78
  return () => window.removeEventListener("keydown", dismissOnEscape, true);
77
79
  }, [draft]);
78
- useEffect(() => subscribeCanvasRendererMessages(({ iframe, message }) => {
80
+ useEffect(() => subscribeCanvasRendererMessages(({ frame, message }) => {
79
81
  if (message.type === "element-click" && isCommentToolActive()) {
80
- const element = iframe.contentDocument?.querySelector(`[${RENDERER_ELEMENT_ID_ATTR}="${message.elementId}"]`);
81
- if (element && element.getAttribute("data-cid") === message.cid && (element.getAttribute("data-src") ?? "") === message.src) {
82
- openDraft({ element, iframe });
83
- }
82
+ const element = frame.element(message);
83
+ if (element)
84
+ openDraft({ element, iframe: frame.iframe });
84
85
  }
85
86
  if (message.type === "element-deselect")
86
87
  setDraft(null);
87
- if (message.type === "frame-ready" && iframe.contentDocument) {
88
- void reconcileComments(iframe.contentDocument).catch(() => undefined);
88
+ const frameDocument = frame.document();
89
+ if (message.type === "frame-ready" && frameDocument) {
90
+ void reconcileComments(frameDocument).catch(() => undefined);
89
91
  }
90
92
  }), []);
91
93
  useEffect(() => {
@@ -239,7 +241,7 @@ export function CommentsOverlay() {
239
241
  }
240
242
  if (mode !== "canvas")
241
243
  return null;
242
- return _jsxs(_Fragment, { children: [_jsx("style", { children: styles }), comments.flatMap((comment) => [...anchors].filter(([key]) => key.startsWith(`${comment.id}:`)).map(([key, anchor]) => (_jsx("button", { type: "button", className: "comment-pin", "data-test": "comment-pin", "aria-label": `Comment: ${comment.note}`, title: comment.note, style: anchor, onClick: () => {
244
+ return _jsxs(_Fragment, { children: [_jsx(InspectorStyle, { css: styles }), comments.flatMap((comment) => [...anchors].filter(([key]) => key.startsWith(`${comment.id}:`)).map(([key, anchor]) => (_jsx("button", { type: "button", className: "comment-pin", "data-test": "comment-pin", "aria-label": `Comment: ${comment.note}`, title: comment.note, style: anchor, onClick: () => {
243
245
  const bound = bindings.current.get(key);
244
246
  if (bound)
245
247
  openDraft({ ...bound, existing: comment });
@@ -1,3 +1,4 @@
1
+ import { inspectorStorage } from "../runtime/inspectorStorage.js";
1
2
  import { useSyncExternalStore } from "react";
2
3
  import { isRenderedInstanceRef } from "../changes/editModel.js";
3
4
  import { useCanvasCards, useFocusedCardId, useSelectedCardId } from "../canvas/canvasStore.js";
@@ -29,7 +30,7 @@ export function getComments() {
29
30
  loadedKey = key;
30
31
  loadedDemo = demo;
31
32
  try {
32
- const saved = demo ? [] : JSON.parse(localStorage.getItem(key) ?? "[]");
33
+ const saved = demo ? [] : JSON.parse(inspectorStorage()?.getItem(key) ?? "[]");
33
34
  comments = Array.isArray(saved) ? saved.filter(isComment) : [];
34
35
  }
35
36
  catch {
@@ -51,7 +52,7 @@ function publish(next) {
51
52
  comments = next;
52
53
  if (getNudgeUiRuntimeConfig().demo !== true) {
53
54
  try {
54
- localStorage.setItem(storageKey(), JSON.stringify(next));
55
+ inspectorStorage()?.setItem(storageKey(), JSON.stringify(next));
55
56
  }
56
57
  catch { }
57
58
  }
@@ -1,4 +1,4 @@
1
- import { getDraftChanges } from "../changes/draftChanges.js";
1
+ import { getDraftChanges } from "../drafts/store.js";
2
2
  import { documentTarget, targetKey } from "../drafts/model.js";
3
3
  import { isCanvasProjectionRevisionCurrent, projectDraftToDocument } from "../canvas/projection.js";
4
4
  import { commentFingerprint, commentViewport, resolveCommentElement } from "./element.js";
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { isComponentChange, useChanges } from "../changes/changesLog.js";
2
+ import { isComponentChange } from "../changes/types.js";
3
+ import { useChanges } from "../drafts/store.js";
3
4
  import { FieldRow } from "../ui/FieldRow.js";
4
5
  import { Select } from "../ui/Select.js";
5
6
  import { SegmentedControl } from "../ui/SegmentedControl.js";
@@ -1,4 +1,4 @@
1
- import { appendChange } from "../changes/changesLog.js";
1
+ import { appendChange } from "../drafts/store.js";
2
2
  import { createComponentPropChange } from "./changeModel.js";
3
3
  export { createComponentPropChange } from "./changeModel.js";
4
4
  export function setComponentProp(target, prop, value) {
@@ -1,6 +1,5 @@
1
1
  import type { ComponentChangeTarget, EditableComponentTarget } from "./types.js";
2
- import type { TextProjectionTarget } from "../inline-text/textChangeBoundary.js";
3
- import type { TextBindingEvidence, TextProjectionScope } from "../inline-text/textChangeBoundary.js";
2
+ import type { TextBindingEvidence, TextProjectionScope, TextProjectionTarget } from "../changes/editModel.js";
4
3
  /** The order used when more than one visible prop has the same value. */
5
4
  export declare const VISIBLE_TEXT_PROP_PRIORITY: readonly ["children", "label", "text", "caption", "description", "title"];
6
5
  export type TextEditRejectionReason = "no-text" | "no-binding" | "ambiguous-binding" | "editing-active" | "unsafe-target";
@@ -1,5 +1,5 @@
1
1
  import { callsiteMultiplicity, editableComponentTargets, inspectComponentTargets, } from "./adapterRegistry.js";
2
- import { captureTextProjectionTarget, resolveTextProjectionTextNode, textProjectionSelector, } from "../projection/textProjection.js";
2
+ import { captureTextProjectionTarget, hasSafeMixedDescendants, isInApplicationEditingHost, isUnsafeTextHost, resolveTextProjectionTextNode, textProjectionSelector, } from "../projection/textProjection.js";
3
3
  /** The order used when more than one visible prop has the same value. */
4
4
  export const VISIBLE_TEXT_PROP_PRIORITY = [
5
5
  "children",
@@ -17,24 +17,6 @@ const STRUCTURAL_TEXT_PROPS = new Set([
17
17
  "role",
18
18
  "style",
19
19
  ]);
20
- const UNSAFE_TEXT_TAGS = new Set(["SCRIPT", "STYLE", "TEXTAREA", "OPTION"]);
21
- const UNSAFE_MIXED_DESCENDANT_TAGS = new Set([
22
- ...UNSAFE_TEXT_TAGS,
23
- "INPUT",
24
- "SELECT",
25
- "BUTTON",
26
- "A",
27
- "B",
28
- "STRONG",
29
- "I",
30
- "EM",
31
- "U",
32
- "MARK",
33
- "CODE",
34
- "S",
35
- "DEL",
36
- "INS",
37
- ]);
38
20
  function isStructuralTextProp(name) {
39
21
  return STRUCTURAL_TEXT_PROPS.has(name)
40
22
  || name.startsWith("data")
@@ -62,9 +44,6 @@ function isApplicationEditingTextNode(element, textNode) {
62
44
  && editingHost.getAttribute("contenteditable") !== "false"
63
45
  && (editingHost === element || element.contains(editingHost) || editingHost.contains(element));
64
46
  }
65
- function isUnsafeSemanticHost(element) {
66
- return UNSAFE_TEXT_TAGS.has(element.tagName);
67
- }
68
47
  function isVisibleTextProp(name) {
69
48
  return VISIBLE_TEXT_PROP_PRIORITY.some((candidate) => candidate === name);
70
49
  }
@@ -107,33 +86,13 @@ function textNodesFor(element) {
107
86
  return nodes;
108
87
  }
109
88
  function isSafeRenderedTextHost(element, textNode) {
110
- if (UNSAFE_TEXT_TAGS.has(element.tagName))
111
- return false;
112
- const editingHost = element.closest("[contenteditable]");
113
- if (editingHost
114
- && editingHost.getAttribute("data-inline-editor") !== "true"
115
- && editingHost.getAttribute("contenteditable") !== "false")
89
+ if (isUnsafeTextHost(element) || isInApplicationEditingHost(element))
116
90
  return false;
117
91
  if (element.children.length === 0) {
118
92
  const directTextNodes = Array.from(element.childNodes).filter((node) => node.nodeType === 3);
119
93
  return directTextNodes.length === 1 && directTextNodes[0] === textNode;
120
94
  }
121
- if (!element.contains(textNode))
122
- return false;
123
- // A line break is layout structure, not rich text content. When the exact
124
- // text node is captured, wrapping that node leaves the <br> in place and
125
- // preserves both lines. Other inline formatting remains rejected because
126
- // replacing text inside it would silently flatten authored markup.
127
- for (const descendant of Array.from(element.querySelectorAll("*"))) {
128
- if (UNSAFE_MIXED_DESCENDANT_TAGS.has(descendant.tagName))
129
- return false;
130
- const descendantEditingHost = descendant.closest("[contenteditable]");
131
- if (descendantEditingHost
132
- && descendantEditingHost.getAttribute("data-inline-editor") !== "true"
133
- && descendantEditingHost.getAttribute("contenteditable") !== "false")
134
- return false;
135
- }
136
- return true;
95
+ return element.contains(textNode) && hasSafeMixedDescendants(element);
137
96
  }
138
97
  function textNodeAtPoint(element, point) {
139
98
  const nodes = textNodesFor(element);
@@ -383,7 +342,7 @@ export function resolveTextBinding(element, point) {
383
342
  // markup is intentionally not included: a confident semantic component
384
343
  // binding may still own a nested text node, while rendered fallback keeps
385
344
  // its stricter direct-leaf requirement.
386
- if (isUnsafeSemanticHost(element)) {
345
+ if (isUnsafeTextHost(element)) {
387
346
  return {
388
347
  kind: "rejected",
389
348
  reason: "unsafe-target",