nudge-ui 0.2.3 → 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 (264) hide show
  1. package/dist/cli/agentCommand.js +4 -28
  2. package/dist/client.mjs +870 -212
  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 +5 -7
  16. package/dist/inspector/agent/verification.js +67 -20
  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 +23 -12
  22. package/dist/inspector/canvas/CanvasElementOverlay.js +97 -128
  23. package/dist/inspector/canvas/CanvasToolbar.js +2 -1
  24. package/dist/inspector/canvas/CanvasWorkspace.js +119 -162
  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/frameScrollbarStyles.d.ts +3 -0
  49. package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
  50. package/dist/inspector/canvas/pageSurface.d.ts +11 -0
  51. package/dist/inspector/canvas/pageSurface.js +34 -0
  52. package/dist/inspector/canvas/projection.d.ts +19 -9
  53. package/dist/inspector/canvas/projection.js +58 -31
  54. package/dist/inspector/canvas/rendererBootstrap.js +4 -1
  55. package/dist/inspector/canvas/rendererElementSelector.js +9 -1
  56. package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
  57. package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
  58. package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
  59. package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
  60. package/dist/inspector/canvas/rendererStylesheet.js +15 -0
  61. package/dist/inspector/canvas/sessionStore.d.ts +2 -2
  62. package/dist/inspector/canvas/sessionStore.js +12 -9
  63. package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
  64. package/dist/inspector/canvas/staleChangeDetector.js +10 -22
  65. package/dist/inspector/changes/codecs.d.ts +2 -0
  66. package/dist/inspector/changes/codecs.js +27 -3
  67. package/dist/inspector/changes/draftPreview.d.ts +12 -0
  68. package/dist/inspector/changes/draftPreview.js +133 -0
  69. package/dist/inspector/changes/editModel.d.ts +18 -0
  70. package/dist/inspector/changes/editModel.js +16 -3
  71. package/dist/inspector/changes/managedStyleProjection.js +2 -1
  72. package/dist/inspector/changes/previewDiagnostics.js +1 -1
  73. package/dist/inspector/changes/types.d.ts +18 -1
  74. package/dist/inspector/comments/CommentsOverlay.js +18 -16
  75. package/dist/inspector/comments/element.js +10 -3
  76. package/dist/inspector/comments/store.js +3 -2
  77. package/dist/inspector/comments/verification.js +1 -1
  78. package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
  79. package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
  80. package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
  81. package/dist/inspector/componentSemantics/changeAction.js +1 -1
  82. package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
  83. package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
  84. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
  85. package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
  86. package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
  87. package/dist/inspector/componentSemantics/textBinding.js +4 -45
  88. package/dist/inspector/componentSemantics/types.d.ts +11 -0
  89. package/dist/inspector/drafts/draftStore.d.ts +109 -0
  90. package/dist/inspector/drafts/draftStore.js +451 -0
  91. package/dist/inspector/drafts/model.d.ts +9 -1
  92. package/dist/inspector/drafts/model.js +5 -1
  93. package/dist/inspector/drafts/store.d.ts +53 -15
  94. package/dist/inspector/drafts/store.js +143 -169
  95. package/dist/inspector/index.d.ts +2 -4
  96. package/dist/inspector/index.js +2 -264
  97. package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
  98. package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
  99. package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
  100. package/dist/inspector/inspection/bridge.d.ts +1 -1
  101. package/dist/inspector/inspection/bridge.js +2 -1
  102. package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
  103. package/dist/inspector/inspection/browserCssInspection.js +28 -6
  104. package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
  105. package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
  106. package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
  107. package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
  108. package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
  109. package/dist/inspector/iterations/capture.d.ts +2 -1
  110. package/dist/inspector/iterations/capture.js +2 -1
  111. package/dist/inspector/iterations/client.d.ts +2 -1
  112. package/dist/inspector/iterations/coordinator.d.ts +4 -3
  113. package/dist/inspector/iterations/coordinator.js +5 -3
  114. package/dist/inspector/iterations/prompt.js +9 -8
  115. package/dist/inspector/overlay/interactionStyles.js +17 -0
  116. package/dist/inspector/pageInspector.d.ts +42 -0
  117. package/dist/inspector/pageInspector.js +342 -0
  118. package/dist/inspector/projection/draftProjection.d.ts +10 -2
  119. package/dist/inspector/projection/draftProjection.js +9 -1
  120. package/dist/inspector/projection/managedStylesheet.js +24 -4
  121. package/dist/inspector/projection/renderedInstance.d.ts +15 -2
  122. package/dist/inspector/projection/renderedInstance.js +60 -7
  123. package/dist/inspector/projection/structuralProjection.d.ts +0 -9
  124. package/dist/inspector/projection/structuralProjection.js +16 -46
  125. package/dist/inspector/projection/textProjection.d.ts +8 -2
  126. package/dist/inspector/projection/textProjection.js +77 -24
  127. package/dist/inspector/prompt/clipboardHandoff.d.ts +14 -0
  128. package/dist/inspector/prompt/clipboardHandoff.js +47 -16
  129. package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
  130. package/dist/inspector/prompt/generatePrompt.js +248 -148
  131. package/dist/inspector/prompt/promptDocument.d.ts +3 -0
  132. package/dist/inspector/prompt/promptDocument.js +8 -0
  133. package/dist/inspector/prompt/promptSettings.js +7 -5
  134. package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
  135. package/dist/inspector/runtime/inspectorStorage.js +42 -0
  136. package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
  137. package/dist/inspector/runtime/ownedStylesheets.js +72 -0
  138. package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
  139. package/dist/inspector/runtime/runtimeConfig.js +10 -1
  140. package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
  141. package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
  142. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
  143. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
  144. package/dist/inspector/selection/editScope.d.ts +1 -0
  145. package/dist/inspector/selection/editScope.js +13 -2
  146. package/dist/inspector/selection/sourceSite.d.ts +12 -0
  147. package/dist/inspector/selection/sourceSite.js +28 -0
  148. package/dist/inspector/shell/AgentStatusButton.js +2 -2
  149. package/dist/inspector/shell/AppShell.js +6 -2
  150. package/dist/inspector/shell/ChangesLog.js +27 -9
  151. package/dist/inspector/shell/CopyPromptButton.js +74 -26
  152. package/dist/inspector/shell/EmptyState.js +3 -3
  153. package/dist/inspector/shell/InspectorShell.d.ts +0 -3
  154. package/dist/inspector/shell/InspectorShell.js +21 -13
  155. package/dist/inspector/shell/PeekOriginalButton.js +3 -3
  156. package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
  157. package/dist/inspector/shell/interactionStatePreview.js +112 -0
  158. package/dist/inspector/shell/originalPreview.d.ts +3 -2
  159. package/dist/inspector/shell/originalPreview.js +10 -37
  160. package/dist/inspector/shell/stateValue.js +2 -3
  161. package/dist/inspector/sketch/SketchEditor.js +5 -4
  162. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
  163. package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
  164. package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
  165. package/dist/inspector/sketch/SketchOverlay.js +139 -263
  166. package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
  167. package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
  168. package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
  169. package/dist/inspector/sketch/SketchThumbnail.js +18 -0
  170. package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
  171. package/dist/inspector/sketch/SketchWorkspace.js +69 -34
  172. package/dist/inspector/sketch/capture.js +19 -3
  173. package/dist/inspector/sketch/copySketch.d.ts +10 -0
  174. package/dist/inspector/sketch/copySketch.js +52 -0
  175. package/dist/inspector/sketch/drawingTools.d.ts +17 -0
  176. package/dist/inspector/sketch/drawingTools.js +47 -0
  177. package/dist/inspector/sketch/frameHeading.d.ts +9 -0
  178. package/dist/inspector/sketch/frameHeading.js +30 -0
  179. package/dist/inspector/sketch/freehand.js +3 -3
  180. package/dist/inspector/sketch/geometry.d.ts +6 -0
  181. package/dist/inspector/sketch/geometry.js +19 -0
  182. package/dist/inspector/sketch/history.d.ts +30 -0
  183. package/dist/inspector/sketch/history.js +58 -0
  184. package/dist/inspector/sketch/interaction.d.ts +1 -3
  185. package/dist/inspector/sketch/interaction.js +6 -9
  186. package/dist/inspector/sketch/model.d.ts +35 -1
  187. package/dist/inspector/sketch/model.js +116 -61
  188. package/dist/inspector/sketch/persistence.d.ts +14 -0
  189. package/dist/inspector/sketch/persistence.js +63 -7
  190. package/dist/inspector/sketch/raster.d.ts +6 -2
  191. package/dist/inspector/sketch/raster.js +43 -17
  192. package/dist/inspector/sketch/store.d.ts +1 -1
  193. package/dist/inspector/sketch/store.js +1 -1
  194. package/dist/inspector/sketch/videoFrames.d.ts +6 -0
  195. package/dist/inspector/sketch/videoFrames.js +81 -0
  196. package/dist/inspector/styleEditors/BackgroundEditor.js +3 -2
  197. package/dist/inspector/styleEditors/BorderEditor.js +30 -130
  198. package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
  199. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
  200. package/dist/inspector/styleEditors/LayoutSection.js +2 -2
  201. package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
  202. package/dist/inspector/styleEditors/borderModel.js +114 -0
  203. package/dist/inspector/styleEditors/layoutValue.js +2 -3
  204. package/dist/inspector/tokens/TokenField.js +40 -7
  205. package/dist/inspector/tokens/TokensPanel.js +2 -1
  206. package/dist/inspector/tokens/editActions.d.ts +3 -3
  207. package/dist/inspector/tokens/editActions.js +15 -3
  208. package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
  209. package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
  210. package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
  211. package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
  212. package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
  213. package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
  214. package/dist/inspector/tokens/resolution.d.ts +6 -3
  215. package/dist/inspector/tokens/resolution.js +91 -112
  216. package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
  217. package/dist/inspector/tokens/tokenEdits.js +1 -1
  218. package/dist/inspector/ui/InlineStyleWarning.js +2 -2
  219. package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
  220. package/dist/inspector/ui/InspectorStyle.js +26 -0
  221. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  222. package/dist/inspector/ui/PopoverListbox.js +3 -3
  223. package/dist/inspector/ui/Toast.d.ts +14 -0
  224. package/dist/inspector/ui/Toast.js +30 -0
  225. package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
  226. package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
  227. package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
  228. package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
  229. package/dist/inspector/ui/customCursors.d.ts +5 -0
  230. package/dist/inspector/ui/customCursors.js +32 -6
  231. package/dist/inspector/ui/styles.js +12 -10
  232. package/dist/inspector/workspace/commands.js +1 -2
  233. package/dist/inspector/workspace/controller.js +1 -2
  234. package/dist/inspector/workspace/handoff.js +3 -2
  235. package/dist/inspector/workspace/sentItems.d.ts +16 -0
  236. package/dist/inspector/workspace/sentItems.js +33 -0
  237. package/dist/inspector/workspace/timeline.d.ts +24 -10
  238. package/dist/inspector/workspace/timeline.js +118 -96
  239. package/dist/internal/agent-protocol.d.ts +50 -13
  240. package/dist/internal/agent-protocol.js +145 -19
  241. package/dist/nudge-ui.mjs +81 -65
  242. package/dist/project/mcpPackage.d.ts +8 -0
  243. package/dist/project/mcpPackage.js +60 -0
  244. package/package.json +13 -3
  245. package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
  246. package/dist/inspector/canvas/measurementProjection.js +0 -16
  247. package/dist/inspector/changes/changesLog.d.ts +0 -47
  248. package/dist/inspector/changes/changesLog.js +0 -247
  249. package/dist/inspector/changes/draftChanges.d.ts +0 -56
  250. package/dist/inspector/changes/draftChanges.js +0 -300
  251. package/dist/inspector/conformance/projection.d.ts +0 -2
  252. package/dist/inspector/conformance/projection.js +0 -3
  253. package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
  254. package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
  255. package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
  256. package/dist/inspector/overlay/InspectorOverlay.js +0 -332
  257. package/dist/inspector/overlay/elementSelector.d.ts +0 -1
  258. package/dist/inspector/overlay/elementSelector.js +0 -111
  259. package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
  260. package/dist/inspector/sketch/SketchChanges.js +0 -72
  261. package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
  262. package/dist/inspector/sketch/SketchEntryButton.js +0 -11
  263. package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
  264. package/dist/inspector/sketch/SketchLayersPanel.js +0 -71
@@ -1,11 +1,12 @@
1
+ import { inspectorStorage } from "../runtime/inspectorStorage.js";
1
2
  import { isFrameContent } from "./frameContent.js";
2
3
  import { clearComments } from "../comments/store.js";
4
+ import { clearSketchesForProject } from "../sketch/store.js";
5
+ import { clearSketchClipboardHandoff } from "../sketch/handoff.js";
3
6
  import { documentTarget, targetFromKey } from "../drafts/model.js";
4
- import { canEditDraft, getActiveDraftChanges, isDraftLocked } from "../changes/draftChanges.js";
5
7
  import { getFrameGroups, isFrameGroup, getCanvasCards, getFocusedCardId, getSelectedCardId, hydrateCanvasStore, removeCanvasCard, } from "./canvasStore.js";
6
8
  import { getCanvasMode, getCanvasPresentation, getBoardCamera, setBoardCamera, } from "./viewStore.js";
7
9
  import { applyRules } from "../projection/managedStylesheet.js";
8
- import { clearActiveDraft } from "../changes/changesLog.js";
9
10
  import { removeManagedSheet } from "../projection/managedStylesheet.js";
10
11
  import { setSelectedElement } from "../selection/selectionStore.js";
11
12
  import { canWriteWorkspace } from "./workspaceLease.js";
@@ -13,7 +14,7 @@ import { resetStructuralDeleteProjection, } from "../projection/structuralProjec
13
14
  import { projectToAllReadyCards } from "./projection.js";
14
15
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
15
16
  import { clearClipboardHandoff, getClipboardHandoffSnapshot, hydrateClipboardHandoff, isClipboardHandoffSnapshot, } from "../prompt/clipboardHandoff.js";
16
- import { activateDraftForCard, clearDrafts, persistDrafts } from "../drafts/store.js";
17
+ import { activateDraftForCard, canEditDraft, clearActiveDraft, clearDrafts, getActiveDraftChanges, isDraftLocked, persistDrafts, } from "../drafts/store.js";
17
18
  const SCHEMA_VERSION = 17;
18
19
  const STORAGE_PREFIX = "nudge-ui";
19
20
  function projectId() {
@@ -79,7 +80,7 @@ function persistSessionUnchecked() {
79
80
  try {
80
81
  persistDrafts();
81
82
  const session = buildSession();
82
- localStorage.setItem(storageKey(projectId()), JSON.stringify(session));
83
+ inspectorStorage()?.setItem(storageKey(projectId()), JSON.stringify(session));
83
84
  return true;
84
85
  }
85
86
  catch {
@@ -92,7 +93,7 @@ export function hydrateSession() {
92
93
  return { restored: false, changeCount: 0 };
93
94
  let raw = null;
94
95
  try {
95
- raw = localStorage.getItem(storageKey(projectId()));
96
+ raw = inspectorStorage()?.getItem(storageKey(projectId())) ?? null;
96
97
  }
97
98
  catch {
98
99
  return { restored: false, changeCount: 0 };
@@ -211,24 +212,26 @@ export function hydrateSession() {
211
212
  }
212
213
  function safeDiscard() {
213
214
  try {
214
- localStorage.removeItem(storageKey(projectId()));
215
+ inspectorStorage()?.removeItem(storageKey(projectId()));
215
216
  }
216
217
  catch {
217
218
  // ignore
218
219
  }
219
220
  }
220
- /** Clears the active frame's edit draft while preserving canvas geometry and other drafts. */
221
+ /** Clears the active draft, its comments, and project sketches while preserving canvas geometry and other drafts. */
221
222
  export function clearSelectedFrameChanges() {
222
223
  if (!canEditDraft())
223
- return;
224
+ return Promise.resolve();
224
225
  const cardId = getSelectedCardId() ?? getFocusedCardId();
225
226
  if (cardId)
226
227
  activateDraftForCard(cardId);
227
228
  clearComments(targetFromKey(getActiveDraftChanges().draftId) ?? documentTarget(window.location.href));
228
229
  clearActiveDraft();
230
+ clearSketchClipboardHandoff();
229
231
  setSelectedElement(null);
230
232
  projectToAllReadyCards();
231
233
  scheduleAutoSave();
234
+ return clearSketchesForProject();
232
235
  }
233
236
  export function clearSession() {
234
237
  if (isDraftLocked())
@@ -236,7 +239,7 @@ export function clearSession() {
236
239
  if (!canWriteWorkspace())
237
240
  return;
238
241
  try {
239
- localStorage.removeItem(storageKey(projectId()));
242
+ inspectorStorage()?.removeItem(storageKey(projectId()));
240
243
  }
241
244
  catch {
242
245
  // ignore
@@ -1,4 +1,4 @@
1
- import type { ChangeRecord } from "../changes/changesLog.js";
1
+ import { type ChangeRecord } from "../changes/types.js";
2
2
  export type StaleChangeStatus = "unverified" | "verified" | "stale" | "token-drift";
3
3
  export interface StaleChangeInfo {
4
4
  index: number;
@@ -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() {
@@ -32,7 +32,9 @@ export interface SerializableElementChange {
32
32
  component: string;
33
33
  };
34
34
  runtimeEvidence?: NonNullable<ElementChangeRecord["runtimeEvidence"]>;
35
+ sourceEvidence?: NonNullable<ElementChangeRecord["sourceEvidence"]>;
35
36
  scope?: EditScope;
37
+ sourceInstanceCount?: number;
36
38
  instanceOverride?: RenderedInstanceOverride;
37
39
  state?: NonNullable<ElementChangeRecord["state"]>;
38
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", "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"
@@ -33,7 +33,9 @@ export function isSerializableElementChangeValue(value) {
33
33
  || (value.rawValue !== undefined && typeof value.rawValue !== "string")
34
34
  || (value.oldRawValue !== undefined && typeof value.oldRawValue !== "string")
35
35
  || (value.runtimeEvidence !== undefined && !isRuntimeElementEvidenceValue(value.runtimeEvidence))
36
+ || (value.sourceEvidence !== undefined && !isSourceElementEvidenceValue(value.sourceEvidence))
36
37
  || !isEditScopeValue(value.scope)
38
+ || (value.sourceInstanceCount !== undefined && (!Number.isSafeInteger(value.sourceInstanceCount) || value.sourceInstanceCount < 1))
37
39
  || (value.instanceOverride !== undefined && value.scope !== "rendered-instance")
38
40
  || (value.scope === "rendered-instance" && !isRenderedInstanceOverride(value.instanceOverride))
39
41
  || (value.state !== undefined && !isElementStateValue(value.state))) {
@@ -115,7 +117,9 @@ export function serializeElementChange(change) {
115
117
  oldRawValue: change.oldRawValue,
116
118
  source: change.source,
117
119
  runtimeEvidence: change.runtimeEvidence ? { ...change.runtimeEvidence } : undefined,
120
+ sourceEvidence: change.sourceEvidence ? { ...change.sourceEvidence } : undefined,
118
121
  scope: change.scope === "rendered-instance" ? "rendered-instance" : "source-site",
122
+ ...(change.sourceInstanceCount !== undefined ? { sourceInstanceCount: change.sourceInstanceCount } : {}),
119
123
  instanceOverride: change.scope === "rendered-instance" ? change.instanceOverride : undefined,
120
124
  state: change.state,
121
125
  };
@@ -161,6 +165,9 @@ export function serializeTextContentChange(change) {
161
165
  props: change.target.props,
162
166
  ariaLabel: change.target.ariaLabel,
163
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 } : {}),
164
171
  ...(change.target.textNodePath?.length
165
172
  ? { textNodePath: [...change.target.textNodePath] }
166
173
  : {}),
@@ -200,7 +207,9 @@ export function deserializeElementChange(serialized) {
200
207
  oldRawValue: serialized.oldRawValue,
201
208
  source: serialized.source,
202
209
  runtimeEvidence: serialized.runtimeEvidence ? { ...serialized.runtimeEvidence } : undefined,
210
+ sourceEvidence: serialized.sourceEvidence ? { ...serialized.sourceEvidence } : undefined,
203
211
  scope: serialized.scope ?? "source-site",
212
+ ...(serialized.sourceInstanceCount !== undefined ? { sourceInstanceCount: serialized.sourceInstanceCount } : {}),
204
213
  instanceOverride: serialized.scope === "rendered-instance" ? serialized.instanceOverride : undefined,
205
214
  state: serialized.state,
206
215
  };
@@ -245,6 +254,9 @@ export function deserializeTextContentChange(serialized) {
245
254
  props: serialized.target.props,
246
255
  ariaLabel: serialized.target.ariaLabel,
247
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 } : {}),
248
260
  ...(serialized.target.textNodePath?.length
249
261
  ? { textNodePath: [...serialized.target.textNodePath] }
250
262
  : {}),
@@ -324,12 +336,24 @@ function isComponentInvocationEvidenceValue(value) {
324
336
  }
325
337
  function isRuntimeElementEvidenceValue(value) {
326
338
  return isRecord(value)
327
- && hasOnlyKeys(value, ["reason", "tagName", "text", "props", "ariaLabel"])
339
+ && hasOnlyKeys(value, ["reason", "tagName", "text", "props", "ariaLabel", "domPath", "elementId"])
328
340
  && (value.reason === undefined || value.reason === "runtime-created" || value.reason === "unannotated")
329
341
  && typeof value.tagName === "string"
330
342
  && (typeof value.text === "string" || value.text === null)
331
343
  && (typeof value.props === "string" || value.props === null)
332
- && (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);
347
+ }
348
+ function isSourceElementEvidenceValue(value) {
349
+ return isRecord(value)
350
+ && hasOnlyKeys(value, ["tagName", "props", "text", "ariaLabel", "domPath", "elementId"])
351
+ && typeof value.tagName === "string"
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);
333
357
  }
334
358
  function isTokenRefValue(value) {
335
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,8 +18,22 @@ 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;
25
+ /**
26
+ * The component invocation whose entire output is this element. Present
27
+ * only when evidence alone cannot tell identical outputs apart.
28
+ */
29
+ invocation?: InvocationRef;
21
30
  };
22
31
  }
32
+ /** A component callsite, as stamped by the compiler, and its component name. */
33
+ export interface InvocationRef {
34
+ callsiteId: string;
35
+ componentName: string;
36
+ }
23
37
  export interface RenderedInstanceOverride {
24
38
  id: string;
25
39
  target: RenderedInstanceRef;
@@ -40,6 +54,10 @@ export interface TextProjectionTarget {
40
54
  props: string | null;
41
55
  ariaLabel: string | null;
42
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;
43
61
  /** Document-local evidence for a text node inside safe mixed markup. */
44
62
  textNodePath?: readonly number[];
45
63
  }
@@ -1,3 +1,9 @@
1
+ function isInvocationRef(value) {
2
+ return isRecord(value)
3
+ && hasOnlyKeys(value, ["callsiteId", "componentName"])
4
+ && typeof value.callsiteId === "string" && value.callsiteId.length > 0 && value.callsiteId.length <= 1024
5
+ && typeof value.componentName === "string" && value.componentName.length > 0 && value.componentName.length <= 256;
6
+ }
1
7
  export function isRenderedInstanceRef(value) {
2
8
  if (!isRecord(value) || !hasOnlyKeys(value, ["sourceSite", "locator"]))
3
9
  return false;
@@ -8,13 +14,17 @@ export function isRenderedInstanceRef(value) {
8
14
  || typeof source.cid !== "string"
9
15
  || typeof source.src !== "string"
10
16
  || !isRecord(locator)
11
- || !hasOnlyKeys(locator, ["kind", "occurrence", "props", "text", "ariaLabel"]))
17
+ || !hasOnlyKeys(locator, ["kind", "occurrence", "props", "text", "ariaLabel", "tagName", "domPath", "elementId", "invocation"]))
12
18
  return false;
13
19
  return locator.kind === "evidence"
14
20
  && Number.isSafeInteger(locator.occurrence) && locator.occurrence >= 0
15
21
  && (typeof locator.props === "string" || locator.props === null)
16
22
  && (typeof locator.text === "string" || locator.text === null)
17
- && (locator.ariaLabel === undefined || typeof locator.ariaLabel === "string" || locator.ariaLabel === null);
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)
27
+ && (locator.invocation === undefined || isInvocationRef(locator.invocation));
18
28
  }
19
29
  export function isRenderedInstanceOverride(value) {
20
30
  return isRecord(value)
@@ -39,7 +49,7 @@ export function isStyleRuleContextValue(value) {
39
49
  }
40
50
  export function isTextProjectionTargetValue(value) {
41
51
  if (!isRecord(value)
42
- || !hasOnlyKeys(value, ["sourceSite", "occurrence", "props", "ariaLabel", "beforeText", "textNodePath"])) {
52
+ || !hasOnlyKeys(value, ["sourceSite", "occurrence", "props", "ariaLabel", "beforeText", "textNodePath", "tagName", "elementId", "domPath"])) {
43
53
  return false;
44
54
  }
45
55
  const occurrence = value.occurrence;
@@ -51,6 +61,9 @@ export function isTextProjectionTargetValue(value) {
51
61
  || (typeof props !== "string" && props !== null)
52
62
  || (typeof ariaLabel !== "string" && ariaLabel !== null)
53
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")
54
67
  || (textNodePath !== undefined
55
68
  && (!Array.isArray(textNodePath)
56
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,6 +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;
18
+ }
19
+ /** Bounded authored facts for one annotated element. */
20
+ export interface SourceElementEvidence {
21
+ tagName: string;
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;
15
28
  }
16
29
  export interface ElementChangeRecord {
17
30
  kind?: "element";
@@ -37,7 +50,11 @@ export interface ElementChangeRecord {
37
50
  };
38
51
  /** Present when the selected element has a document-local runtime identity. */
39
52
  runtimeEvidence?: RuntimeElementEvidence;
53
+ /** Authored facts that relocate the source site after its position shifts. */
54
+ sourceEvidence?: SourceElementEvidence;
40
55
  scope?: EditScope;
56
+ /** Matching rendered items when captured; presentation only, absent in older edits. */
57
+ sourceInstanceCount?: number;
41
58
  /** Durable, controller-owned target for one rendered output. */
42
59
  instanceOverride?: RenderedInstanceOverride;
43
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,17 +1,24 @@
1
1
  import { resolveRenderedInstance } from "../projection/renderedInstance.js";
2
- import { sourceSiteSelector } from "../selection/sourceSite.js";
2
+ import { relocateSourceSite, sourceSiteSelector } from "../selection/sourceSite.js";
3
3
  /** Retains a changed target only when its source site still identifies one output. */
4
4
  export function resolveCommentElement(doc, target) {
5
5
  const exact = resolveRenderedInstance(doc, target);
6
6
  if (exact.status === "resolved")
7
7
  return exact.element;
8
- const selector = sourceSiteSelector(target.sourceSite.cid, target.sourceSite.src);
8
+ const { props, ariaLabel } = target.locator;
9
+ const sourceSite = relocateSourceSite(doc, target.sourceSite, (element) => element.getAttribute("data-cprops") === props && element.getAttribute("aria-label") === (ariaLabel ?? null));
10
+ if (sourceSite) {
11
+ const moved = resolveRenderedInstance(doc, { ...target, sourceSite });
12
+ if (moved.status === "resolved")
13
+ return moved.element;
14
+ }
15
+ const site = sourceSite ?? target.sourceSite;
16
+ const selector = sourceSiteSelector(site.cid, site.src);
9
17
  if (!selector)
10
18
  return null;
11
19
  const candidates = Array.from(doc.querySelectorAll(selector));
12
20
  if (candidates.length === 1)
13
21
  return candidates[0];
14
- const { props, ariaLabel } = target.locator;
15
22
  if (!props && !ariaLabel)
16
23
  return null;
17
24
  const matching = candidates.filter((element) => element.getAttribute("data-cprops") === props