nudge-ui 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/bin/nudge-ui.mjs +3 -1
  2. package/dist/cli/agentCommand.d.ts +8 -0
  3. package/dist/cli/agentCommand.js +59 -0
  4. package/dist/client.mjs +878 -11
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
  6. package/dist/hosts/astro/clientTransport.js +63 -11
  7. package/dist/hosts/astro/integration.d.ts +2 -2
  8. package/dist/hosts/astro/integration.js +4 -3
  9. package/dist/hosts/environment.d.ts +15 -0
  10. package/dist/hosts/environment.js +16 -0
  11. package/dist/hosts/next/sidecar.d.ts +4 -0
  12. package/dist/hosts/next/sidecar.js +56 -7
  13. package/dist/hosts/next/wrapper.d.ts +2 -0
  14. package/dist/hosts/next/wrapper.js +32 -3
  15. package/dist/hosts/projectBridge.d.ts +21 -0
  16. package/dist/hosts/projectBridge.js +98 -0
  17. package/dist/hosts/static/cli.d.ts +1 -1
  18. package/dist/hosts/static/cli.js +6 -1
  19. package/dist/hosts/static/manifest.d.ts +1 -1
  20. package/dist/hosts/static/manifest.js +3 -1
  21. package/dist/hosts/static/server.js +45 -3
  22. package/dist/hosts/vite/vite.d.ts +6 -1
  23. package/dist/hosts/vite/vite.js +82 -15
  24. package/dist/inspector/agent/McpConnectionDialog.d.ts +5 -4
  25. package/dist/inspector/agent/McpConnectionDialog.js +20 -37
  26. package/dist/inspector/agent/client.d.ts +16 -3
  27. package/dist/inspector/agent/client.js +114 -11
  28. package/dist/inspector/agent/connectionStatus.d.ts +3 -3
  29. package/dist/inspector/agent/connectionStatus.js +11 -4
  30. package/dist/inspector/agent/httpTransport.d.ts +8 -0
  31. package/dist/inspector/agent/httpTransport.js +78 -18
  32. package/dist/inspector/agent/protocol.d.ts +11 -3
  33. package/dist/inspector/agent/protocol.js +1 -1
  34. package/dist/inspector/agent/verification.d.ts +1 -1
  35. package/dist/inspector/agent/verification.js +69 -12
  36. package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
  37. package/dist/inspector/canvas/CanvasCard.js +181 -91
  38. package/dist/inspector/canvas/CanvasElementOverlay.js +288 -52
  39. package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
  40. package/dist/inspector/canvas/CanvasToolbar.js +24 -0
  41. package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
  42. package/dist/inspector/canvas/CanvasWorkspace.js +410 -72
  43. package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
  44. package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
  45. package/dist/inspector/canvas/canvasResize.d.ts +30 -0
  46. package/dist/inspector/canvas/canvasResize.js +52 -0
  47. package/dist/inspector/canvas/canvasStore.d.ts +30 -6
  48. package/dist/inspector/canvas/canvasStore.js +128 -61
  49. package/dist/inspector/canvas/frameProtocol.d.ts +82 -5
  50. package/dist/inspector/canvas/frameProtocol.js +63 -2
  51. package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -0
  52. package/dist/inspector/canvas/keyboardShortcuts.js +16 -0
  53. package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
  54. package/dist/inspector/canvas/linkEligibility.js +0 -29
  55. package/dist/inspector/canvas/projection.d.ts +7 -0
  56. package/dist/inspector/canvas/projection.js +118 -25
  57. package/dist/inspector/canvas/rendererBootstrap.js +42 -29
  58. package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
  59. package/dist/inspector/canvas/rendererElementSelector.js +385 -58
  60. package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
  61. package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
  62. package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
  63. package/dist/inspector/canvas/sessionStore.d.ts +1 -0
  64. package/dist/inspector/canvas/sessionStore.js +12 -4
  65. package/dist/inspector/canvas/staleChangeDetector.js +16 -14
  66. package/dist/inspector/changes/changesLog.js +31 -1
  67. package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
  68. package/dist/inspector/changes/managedStyleProjection.js +3 -3
  69. package/dist/inspector/changes/structuralTypes.d.ts +4 -0
  70. package/dist/inspector/changes/workspaceChanges.d.ts +4 -0
  71. package/dist/inspector/changes/workspaceChanges.js +29 -3
  72. package/dist/inspector/clientManifest.d.ts +7 -0
  73. package/dist/inspector/clientManifest.js +12 -0
  74. package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
  75. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
  76. package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
  77. package/dist/inspector/index.d.ts +2 -0
  78. package/dist/inspector/index.js +80 -32
  79. package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
  80. package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
  81. package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
  82. package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
  83. package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
  84. package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
  85. package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
  86. package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
  87. package/dist/inspector/inline-text/textEditChange.js +165 -0
  88. package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
  89. package/dist/inspector/inspection/selectionProperty.js +1 -0
  90. package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
  91. package/dist/inspector/overlay/DomNavigator.js +40 -0
  92. package/dist/inspector/overlay/InspectorOverlay.js +6 -2
  93. package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
  94. package/dist/inspector/overlay/clickPolicy.js +34 -3
  95. package/dist/inspector/overlay/elementSelector.d.ts +1 -1
  96. package/dist/inspector/overlay/elementSelector.js +18 -7
  97. package/dist/inspector/overlay/selectedGeometry.js +21 -2
  98. package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
  99. package/dist/inspector/overlay/spacingGestures.js +351 -0
  100. package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
  101. package/dist/inspector/overlay/structuralGestures.js +12 -10
  102. package/dist/inspector/projection/managedStylesheet.js +4 -1
  103. package/dist/inspector/projection/renderedInstance.d.ts +2 -0
  104. package/dist/inspector/projection/renderedInstance.js +5 -1
  105. package/dist/inspector/projection/structuralProjection.d.ts +2 -0
  106. package/dist/inspector/projection/structuralProjection.js +85 -1
  107. package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
  108. package/dist/inspector/projection/textProjection.d.ts +2 -0
  109. package/dist/inspector/projection/textProjection.js +10 -4
  110. package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
  111. package/dist/inspector/prompt/clipboardHandoff.js +3 -3
  112. package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
  113. package/dist/inspector/prompt/generatePrompt.js +19 -2
  114. package/dist/inspector/runtime/editorShell.d.ts +2 -0
  115. package/dist/inspector/runtime/editorShell.js +4 -0
  116. package/dist/inspector/runtime/runtimeConfig.d.ts +5 -1
  117. package/dist/inspector/runtime/runtimeConfig.js +4 -0
  118. package/dist/inspector/selection/hierarchy.d.ts +6 -0
  119. package/dist/inspector/selection/hierarchy.js +8 -0
  120. package/dist/inspector/selection/selectionStore.js +2 -0
  121. package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
  122. package/dist/inspector/settings/SettingsDialog.js +12 -2
  123. package/dist/inspector/shell/AppShell.js +5 -1
  124. package/dist/inspector/shell/ChangesLog.js +26 -22
  125. package/dist/inspector/shell/CopyPromptButton.js +102 -19
  126. package/dist/inspector/shell/EmptyState.js +1 -1
  127. package/dist/inspector/shell/InspectorShell.d.ts +0 -1
  128. package/dist/inspector/shell/InspectorShell.js +38 -42
  129. package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
  130. package/dist/inspector/shell/PeekOriginalButton.js +57 -0
  131. package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
  132. package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
  133. package/dist/inspector/shell/originalPreview.d.ts +9 -0
  134. package/dist/inspector/shell/originalPreview.js +61 -0
  135. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  136. package/dist/inspector/shell/shortcuts.js +15 -0
  137. package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
  138. package/dist/inspector/sketch/SketchChanges.js +72 -0
  139. package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
  140. package/dist/inspector/sketch/SketchEditor.js +402 -0
  141. package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
  142. package/dist/inspector/sketch/SketchEntryButton.js +11 -0
  143. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
  144. package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
  145. package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
  146. package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
  147. package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
  148. package/dist/inspector/sketch/SketchOverlay.js +420 -0
  149. package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
  150. package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
  151. package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
  152. package/dist/inspector/sketch/SketchWorkspace.js +152 -0
  153. package/dist/inspector/sketch/capture.d.ts +55 -0
  154. package/dist/inspector/sketch/capture.js +350 -0
  155. package/dist/inspector/sketch/freehand.d.ts +13 -0
  156. package/dist/inspector/sketch/freehand.js +64 -0
  157. package/dist/inspector/sketch/geometry.d.ts +17 -0
  158. package/dist/inspector/sketch/geometry.js +98 -0
  159. package/dist/inspector/sketch/handoff.d.ts +21 -0
  160. package/dist/inspector/sketch/handoff.js +72 -0
  161. package/dist/inspector/sketch/interaction.d.ts +21 -0
  162. package/dist/inspector/sketch/interaction.js +98 -0
  163. package/dist/inspector/sketch/model.d.ts +104 -0
  164. package/dist/inspector/sketch/model.js +282 -0
  165. package/dist/inspector/sketch/persistence.d.ts +21 -0
  166. package/dist/inspector/sketch/persistence.js +205 -0
  167. package/dist/inspector/sketch/raster.d.ts +22 -0
  168. package/dist/inspector/sketch/raster.js +203 -0
  169. package/dist/inspector/sketch/sketchNote.d.ts +10 -0
  170. package/dist/inspector/sketch/sketchNote.js +30 -0
  171. package/dist/inspector/sketch/store.d.ts +47 -0
  172. package/dist/inspector/sketch/store.js +434 -0
  173. package/dist/inspector/styleEditors/BorderEditor.js +15 -19
  174. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
  175. package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
  176. package/dist/inspector/styleEditors/ColorPicker.js +11 -36
  177. package/dist/inspector/styleEditors/GapField.d.ts +3 -2
  178. package/dist/inspector/styleEditors/GapField.js +2 -2
  179. package/dist/inspector/styleEditors/GridSection.js +1 -1
  180. package/dist/inspector/styleEditors/LayoutSection.js +11 -8
  181. package/dist/inspector/styleEditors/SpacingBox.js +31 -33
  182. package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
  183. package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
  184. package/dist/inspector/styleEditors/layoutValue.js +3 -0
  185. package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
  186. package/dist/inspector/styleEditors/nudgeValue.js +93 -5
  187. package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
  188. package/dist/inspector/styleEditors/stylePresence.js +22 -0
  189. package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
  190. package/dist/inspector/styleEditors/textRelevance.js +35 -0
  191. package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
  192. package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
  193. package/dist/inspector/tokens/TokenField.js +130 -7
  194. package/dist/inspector/tokens/TokensPanel.js +2 -1
  195. package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
  196. package/dist/inspector/tokens/dragPointerLock.js +70 -0
  197. package/dist/inspector/tokens/editActions.d.ts +2 -0
  198. package/dist/inspector/tokens/editActions.js +2 -2
  199. package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
  200. package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
  201. package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
  202. package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
  203. package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
  204. package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
  205. package/dist/inspector/tokens/resolution.d.ts +3 -26
  206. package/dist/inspector/tokens/resolution.js +44 -735
  207. package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
  208. package/dist/inspector/tokens/tokenAvailability.js +204 -0
  209. package/dist/inspector/ui/Button.d.ts +1 -1
  210. package/dist/inspector/ui/Disclosure.d.ts +22 -0
  211. package/dist/inspector/ui/Disclosure.js +13 -0
  212. package/dist/inspector/ui/SideValuesField.js +2 -2
  213. package/dist/inspector/ui/StatusCallout.d.ts +4 -1
  214. package/dist/inspector/ui/StatusCallout.js +2 -2
  215. package/dist/inspector/ui/dragCursor.d.ts +8 -0
  216. package/dist/inspector/ui/dragCursor.js +21 -0
  217. package/dist/inspector/ui/styles.js +21 -17
  218. package/dist/internal/agent-protocol.d.ts +65 -1
  219. package/dist/internal/agent-protocol.js +183 -3
  220. package/dist/nudge-ui.mjs +276 -58
  221. package/dist/transport/editor.d.ts +47 -0
  222. package/dist/transport/editor.js +163 -0
  223. package/dist/transport/index.d.ts +1 -0
  224. package/dist/transport/index.js +1 -0
  225. package/dist/transport/routes.d.ts +10 -0
  226. package/dist/transport/routes.js +10 -0
  227. package/package.json +3 -1
  228. package/dist/inspector/shell/DomNavigation.d.ts +0 -8
  229. package/dist/inspector/shell/DomNavigation.js +0 -30
  230. package/dist/inspector/shell/panelLayout.d.ts +0 -2
  231. package/dist/inspector/shell/panelLayout.js +0 -29
@@ -1,7 +1,15 @@
1
1
  import { isDocumentProjectionReport } from "../projection/renderedInstance.js";
2
2
  import { isTextProjectionReport } from "../projection/textProjection.js";
3
3
  import { isStructuralProjectionReport } from "../projection/structuralProjectionBoundary.js";
4
- // v15 adds renderer-to-controller element deselection for Canvas frames. v14
4
+ // v20 adds Shift state to element drag messages so spacing handles can snap
5
+ // changes to 8px increments.
6
+ // v19 adds spacing drag gestures (hover point, spacing descriptor, start
7
+ // point, and cancelled drag-end) plus renderer-to-controller keyboard shortcut
8
+ // forwarding for Canvas frames.
9
+ // v18 adds parent-to-renderer Alt modifier forwarding for Canvas measurements.
10
+ // v17 adds an idempotent renderer request to open the parent inspector. v16
11
+ // adds controller-owned inline-text intents for Canvas frames. v15 adds
12
+ // renderer-to-controller element deselection for Canvas frames. v14
5
13
  // adds border widths to hover geometry so containment measurements can exclude
6
14
  // the container border. v13 adds source-parent-aware structural moves and
7
15
  // bounded structural failure reasons to the full projection snapshot. v12
@@ -9,7 +17,7 @@ import { isStructuralProjectionReport } from "../projection/structuralProjection
9
17
  // rereading a Canvas iframe until the renderer has applied its revision. v11
10
18
  // added the renderer-hello handshake solicitation for runtimes whose boot
11
19
  // completes after the controller's load-time parent-ready.
12
- export const PROTOCOL_VERSION = 15;
20
+ export const PROTOCOL_VERSION = 20;
13
21
  let rendererIdentity = null;
14
22
  export function setRendererIdentity(identity) {
15
23
  rendererIdentity = identity;
@@ -30,6 +38,26 @@ export function isRendererMessageFor(value, identity) {
30
38
  && message.workspaceId === identity.workspaceId
31
39
  && message.cardId === identity.cardId;
32
40
  }
41
+ export function isKeyboardShortcutCode(value) {
42
+ return value === "Backslash"
43
+ || value === "KeyI"
44
+ || value === "KeyV"
45
+ || value === "KeyH"
46
+ || value === "KeyP"
47
+ || value === "KeyS";
48
+ }
49
+ /** Strict JSON-only schema for renderer-originated global keyboard shortcuts. */
50
+ export function isKeyboardShortcutMessage(value, identity) {
51
+ if (!isRendererMessageFor(value, identity) || !isProtocolObject(value))
52
+ return false;
53
+ if (!hasOnlyKeys(value, [
54
+ "type", "protocolVersion", "projectId", "workspaceId", "cardId", "phase", "code",
55
+ ]))
56
+ return false;
57
+ return ownValue(value, "type") === "keyboard-shortcut"
58
+ && (ownValue(value, "phase") === "keydown" || ownValue(value, "phase") === "keyup")
59
+ && isKeyboardShortcutCode(ownValue(value, "code"));
60
+ }
33
61
  /** Strict JSON-only schema for selecting an element from a renderer frame. */
34
62
  export function isElementClickMessage(value, identity) {
35
63
  if (!isRendererMessageFor(value, identity) || !isProtocolObject(value))
@@ -51,6 +79,31 @@ export function isElementClickMessage(value, identity) {
51
79
  && typeof ownValue(value, "component") === "string"
52
80
  && (additive === undefined || typeof additive === "boolean");
53
81
  }
82
+ /** Strict JSON-only schema for starting controller-owned inline text editing. */
83
+ export function isInlineTextIntentMessage(value, identity) {
84
+ if (!isRendererMessageFor(value, identity) || !isProtocolObject(value))
85
+ return false;
86
+ if (!hasOnlyKeys(value, [
87
+ "type", "protocolVersion", "projectId", "workspaceId", "cardId",
88
+ "intent", "cid", "src", "elementId", "point", "clickCount", "emptyProjectionId",
89
+ ]))
90
+ return false;
91
+ const intent = ownValue(value, "intent");
92
+ const point = ownValue(value, "point");
93
+ const clickCount = ownValue(value, "clickCount");
94
+ const emptyProjectionId = ownValue(value, "emptyProjectionId");
95
+ return ownValue(value, "type") === "inline-text-intent"
96
+ && (intent === "pointer-down" || intent === "double-click")
97
+ && typeof ownValue(value, "cid") === "string"
98
+ && typeof ownValue(value, "src") === "string"
99
+ && typeof ownValue(value, "elementId") === "string"
100
+ && isProtocolObject(point)
101
+ && hasOnlyKeys(point, ["x", "y"])
102
+ && typeof ownValue(point, "x") === "number" && Number.isFinite(ownValue(point, "x"))
103
+ && typeof ownValue(point, "y") === "number" && Number.isFinite(ownValue(point, "y"))
104
+ && (clickCount === undefined || (typeof clickCount === "number" && Number.isSafeInteger(clickCount) && clickCount >= 0))
105
+ && (emptyProjectionId === undefined || typeof emptyProjectionId === "string");
106
+ }
54
107
  /** Strict JSON-only schema for renderer diagnostics before the parent records them. */
55
108
  export function isStructuralProjectionReportMessage(value, identity) {
56
109
  if (!isRendererMessageFor(value, identity) || !value || typeof value !== "object")
@@ -137,6 +190,7 @@ function isProtocolObject(value) {
137
190
  function ownValue(value, key) {
138
191
  switch (key) {
139
192
  case "additive": return value.additive;
193
+ case "emptyProjectionId": return value.emptyProjectionId;
140
194
  case "cardId": return value.cardId;
141
195
  case "cid": return value.cid;
142
196
  case "callsiteId": return value.callsiteId;
@@ -144,6 +198,11 @@ function ownValue(value, key) {
144
198
  case "elementId": return value.elementId;
145
199
  case "file": return value.file;
146
200
  case "line": return value.line;
201
+ case "intent": return value.intent;
202
+ case "point": return value.point;
203
+ case "clickCount": return value.clickCount;
204
+ case "x": return value.x;
205
+ case "y": return value.y;
147
206
  case "prop": return value.prop;
148
207
  case "projectId": return value.projectId;
149
208
  case "protocolVersion": return value.protocolVersion;
@@ -153,6 +212,8 @@ function ownValue(value, key) {
153
212
  case "selector": return value.selector;
154
213
  case "src": return value.src;
155
214
  case "component": return value.component;
215
+ case "code": return value.code;
216
+ case "phase": return value.phase;
156
217
  case "value": return value.value;
157
218
  case "workspaceId": return value.workspaceId;
158
219
  }
@@ -0,0 +1,4 @@
1
+ /** `select` leaves the application interactive; `design` turns clicks into inspector selection. */
2
+ export type CanvasInteractionTool = "select" | "design" | "pan" | "sketch";
3
+ export declare function canvasToolForCode(code: string): CanvasInteractionTool | null;
4
+ export declare function canvasToolForEvent(event: KeyboardEvent): CanvasInteractionTool | null;
@@ -0,0 +1,16 @@
1
+ export function canvasToolForCode(code) {
2
+ if (code === "KeyI")
3
+ return "select";
4
+ if (code === "KeyV")
5
+ return "design";
6
+ if (code === "KeyH")
7
+ return "pan";
8
+ if (code === "KeyP")
9
+ return "sketch";
10
+ return null;
11
+ }
12
+ export function canvasToolForEvent(event) {
13
+ if (event.repeat || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
14
+ return null;
15
+ return canvasToolForCode(event.code);
16
+ }
@@ -1,8 +1,3 @@
1
1
  export declare function findClosestAnchor(target: EventTarget | null): HTMLAnchorElement | null;
2
2
  export declare function isEligibleNavigation(anchor: HTMLAnchorElement, event: MouseEvent): boolean;
3
3
  export declare function hasDifferentRoute(anchor: HTMLAnchorElement): boolean;
4
- /**
5
- * Returns whether the renderer should leave an anchor's browser behavior
6
- * intact instead of consuming it as an editing click.
7
- */
8
- export declare function shouldPreserveNativeLinkActivation(anchor: HTMLAnchorElement, event: MouseEvent): boolean;
@@ -37,32 +37,3 @@ export function hasDifferentRoute(anchor) {
37
37
  return false;
38
38
  }
39
39
  }
40
- /**
41
- * Returns whether the renderer should leave an anchor's browser behavior
42
- * intact instead of consuming it as an editing click.
43
- */
44
- export function shouldPreserveNativeLinkActivation(anchor, event) {
45
- // Command/Ctrl-click is the deep-selection gesture. Shift makes it the
46
- // explicit application-activation gesture instead.
47
- if (event.metaKey || event.ctrlKey)
48
- return event.shiftKey;
49
- // Shift-click is the additive inspector-selection gesture. Do not let a
50
- // link navigate or preserve its native activation in that mode.
51
- if (event.shiftKey)
52
- return false;
53
- if (anchor.hasAttribute("download"))
54
- return true;
55
- if (anchor.target && anchor.target !== "" && anchor.target !== "_self")
56
- return true;
57
- if (anchor.protocol !== "http:" && anchor.protocol !== "https:")
58
- return true;
59
- try {
60
- const url = new URL(anchor.href);
61
- if (url.origin !== window.location.origin)
62
- return true;
63
- }
64
- catch {
65
- return true;
66
- }
67
- return !hasDifferentRoute(anchor);
68
- }
@@ -1,3 +1,4 @@
1
+ import { type WorkspaceContents } from "../changes/workspaceChanges.js";
1
2
  import type { CanvasCard } from "./canvasStore.js";
2
3
  import { type PreviewDocument } from "../changes/previewDiagnostics.js";
3
4
  import { type CompiledManagedStyles } from "../projection/workspaceProjection.js";
@@ -23,6 +24,12 @@ export declare function computeProjection(): {
23
24
  };
24
25
  export declare function resetProjectionRevision(): void;
25
26
  export declare function sendProjectionToCard(card: CanvasCard, iframe: HTMLIFrameElement): void;
27
+ /** Temporarily projects a snapshot through the renderer that owns the document. */
28
+ export declare function projectWorkspaceSnapshotToDocument(doc: Document, snapshot: WorkspaceContents): Promise<number | null>;
29
+ /** Returns whether an acknowledged temporary projection is still authoritative. */
30
+ export declare function isCanvasProjectionRevisionCurrent(doc: Document, expectedRevision: number): boolean;
31
+ /** Returns whether the live frame acknowledged its latest canonical workspace projection. */
32
+ export declare function isCanvasCanonicalProjectionRevisionCurrent(doc: Document, expectedRevision: number): boolean;
26
33
  export declare function registerCardFrameSource(cardId: string, iframe: HTMLIFrameElement): void;
27
34
  export declare function registerCardFrame(cardId: string, iframe: HTMLIFrameElement): void;
28
35
  export declare function unregisterCardFrame(cardId: string): void;
@@ -1,16 +1,18 @@
1
1
  import { getWorkspaceChanges } from "../changes/workspaceChanges.js";
2
2
  import { getCanvasMode } from "./canvasStore.js";
3
3
  import { PROTOCOL_VERSION } from "./frameProtocol.js";
4
- import { clearCanvasRenderedInstanceProjectionReports, } from "../projection/renderedInstance.js";
5
- import { clearCanvasTextProjectionReports, } from "../projection/textProjection.js";
4
+ import { clearCanvasRenderedInstanceProjectionReports, setCanonicalRenderedInstanceOverrides, } from "../projection/renderedInstance.js";
5
+ import { clearCanvasTextProjectionReports, setCanonicalTextContentChanges, } from "../projection/textProjection.js";
6
6
  import { clearCanvasStructuralProjectionReports } from "../projection/structuralProjection.js";
7
7
  import { invalidatePreviewDocumentSession, startPreviewDocumentSession, } from "../changes/previewDiagnostics.js";
8
- import { applyHostWorkspaceProjection, compileWorkspaceProjection, } from "../projection/workspaceProjection.js";
8
+ import { compileWorkspaceProjection, } from "../projection/workspaceProjection.js";
9
+ import { isOriginalPreviewActive } from "../shell/originalPreview.js";
9
10
  import { disposeBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
10
11
  export const PROJECT_ID = window.location.origin;
11
12
  export const WORKSPACE_ID = crypto.randomUUID?.() ?? `ws-${Date.now()}`;
12
13
  let revision = 0;
13
14
  let lastRulesKey = null;
15
+ let lastPeekActive = false;
14
16
  const frameProjectionStates = new Map();
15
17
  const previewDocuments = new Map();
16
18
  const projectionAcknowledgementListeners = new Set();
@@ -49,7 +51,32 @@ function projectionKey(plan) {
49
51
  }
50
52
  export function computeProjection() {
51
53
  const plan = compileWorkspaceProjection(getWorkspaceChanges());
52
- applyHostWorkspaceProjection(plan);
54
+ if (isOriginalPreviewActive()) {
55
+ // Hold-to-view-original: frames show the page without inspector changes
56
+ // while canonical intent (and its projection key) stays untouched.
57
+ if (!lastPeekActive) {
58
+ lastPeekActive = true;
59
+ revision += 1;
60
+ }
61
+ return {
62
+ ...plan,
63
+ managedStyles: { rules: [], css: "" },
64
+ css: "",
65
+ instanceOverrides: [],
66
+ structuralChanges: [],
67
+ textContentChanges: [],
68
+ componentOverrides: [],
69
+ revision,
70
+ };
71
+ }
72
+ if (lastPeekActive) {
73
+ // Force the canonical projection to resend after a peek even though its
74
+ // key is unchanged since the peek began.
75
+ lastPeekActive = false;
76
+ lastRulesKey = null;
77
+ }
78
+ setCanonicalRenderedInstanceOverrides(plan.instanceOverrides);
79
+ setCanonicalTextContentChanges(plan.textContentChanges);
53
80
  const key = projectionKey(plan);
54
81
  if (key !== lastRulesKey) {
55
82
  lastRulesKey = key;
@@ -60,9 +87,11 @@ export function computeProjection() {
60
87
  export function resetProjectionRevision() {
61
88
  revision = 0;
62
89
  lastRulesKey = null;
90
+ lastPeekActive = false;
63
91
  for (const state of frameProjectionStates.values()) {
64
92
  state.sentRevision = -1;
65
93
  state.appliedRevision = -1;
94
+ state.canonicalSentRevision = -1;
66
95
  }
67
96
  projectionAcknowledgementVersion += 1;
68
97
  notifyProjectionAcknowledgementListeners();
@@ -72,12 +101,18 @@ export function sendProjectionToCard(card, iframe) {
72
101
  if (!win)
73
102
  return;
74
103
  const { css, revision: rev, instanceOverrides, structuralChanges, textContentChanges, componentOverrides } = computeProjection();
104
+ sendProjectionMessage(card.id, iframe, rev, css, instanceOverrides, structuralChanges, textContentChanges, componentOverrides, true);
105
+ }
106
+ function sendProjectionMessage(cardId, iframe, rev, css, instanceOverrides, structuralChanges, textContentChanges, componentOverrides, canonical = false) {
107
+ const win = iframe.contentWindow;
108
+ if (!win)
109
+ return;
75
110
  const msg = {
76
111
  type: "replace-styles",
77
112
  protocolVersion: PROTOCOL_VERSION,
78
113
  projectId: PROJECT_ID,
79
114
  workspaceId: WORKSPACE_ID,
80
- cardId: card.id,
115
+ cardId,
81
116
  css,
82
117
  revision: rev,
83
118
  instanceOverrides: [...instanceOverrides],
@@ -85,9 +120,79 @@ export function sendProjectionToCard(card, iframe) {
85
120
  textContentChanges: [...textContentChanges],
86
121
  componentOverrides: [...componentOverrides],
87
122
  };
88
- markProjectionSent(card.id, iframe, rev);
123
+ markProjectionSent(cardId, iframe, rev, canonical);
89
124
  win.postMessage(msg, window.location.origin);
90
125
  }
126
+ /** Temporarily projects a snapshot through the renderer that owns the document. */
127
+ export async function projectWorkspaceSnapshotToDocument(doc, snapshot) {
128
+ const entry = [...frameProjectionStates.entries()].find(([, state]) => state.document === doc);
129
+ if (!entry)
130
+ return null;
131
+ const [cardId, state] = entry;
132
+ const plan = compileWorkspaceProjection({ ...snapshot, revision: 0 });
133
+ revision += 1;
134
+ sendProjectionMessage(cardId, state.iframe, revision, plan.managedStyles.css, plan.instanceOverrides, plan.structuralChanges, plan.textContentChanges, plan.componentOverrides);
135
+ if (state.appliedRevision === revision && state.sentRevision === revision)
136
+ return revision;
137
+ const requestedRevision = revision;
138
+ return new Promise((resolve) => {
139
+ let settled = false;
140
+ let timer;
141
+ const finish = (appliedRevision) => {
142
+ if (settled)
143
+ return;
144
+ settled = true;
145
+ unsubscribe();
146
+ if (timer !== undefined)
147
+ clearTimeout(timer);
148
+ resolve(appliedRevision);
149
+ };
150
+ const unsubscribe = subscribeCanvasProjectionAcknowledgements(() => {
151
+ const current = frameProjectionStates.get(cardId);
152
+ if (!current || current.document !== doc)
153
+ finish(null);
154
+ else if (current.sentRevision > requestedRevision || current.appliedRevision > requestedRevision)
155
+ finish(null);
156
+ else if (current.appliedRevision === requestedRevision)
157
+ finish(requestedRevision);
158
+ });
159
+ timer = setTimeout(() => finish(null), 2_000);
160
+ });
161
+ }
162
+ /** Returns whether an acknowledged temporary projection is still authoritative. */
163
+ export function isCanvasProjectionRevisionCurrent(doc, expectedRevision) {
164
+ for (const state of frameProjectionStates.values()) {
165
+ if (state.document !== doc)
166
+ continue;
167
+ try {
168
+ if (state.iframe.contentDocument !== doc)
169
+ return false;
170
+ }
171
+ catch {
172
+ return false;
173
+ }
174
+ return state.sentRevision === expectedRevision && state.appliedRevision === expectedRevision;
175
+ }
176
+ return false;
177
+ }
178
+ /** Returns whether the live frame acknowledged its latest canonical workspace projection. */
179
+ export function isCanvasCanonicalProjectionRevisionCurrent(doc, expectedRevision) {
180
+ for (const state of frameProjectionStates.values()) {
181
+ if (state.document !== doc)
182
+ continue;
183
+ try {
184
+ if (state.iframe.contentDocument !== doc)
185
+ return false;
186
+ }
187
+ catch {
188
+ return false;
189
+ }
190
+ return state.canonicalSentRevision === expectedRevision
191
+ && state.sentRevision === expectedRevision
192
+ && state.appliedRevision === expectedRevision;
193
+ }
194
+ return false;
195
+ }
91
196
  const frameRegistry = new Map();
92
197
  const frameSourceRegistry = new Map();
93
198
  export function registerCardFrameSource(cardId, iframe) {
@@ -109,6 +214,7 @@ export function registerCardFrame(cardId, iframe) {
109
214
  previewDocument,
110
215
  sentRevision: -1,
111
216
  appliedRevision: -1,
217
+ canonicalSentRevision: -1,
112
218
  });
113
219
  }
114
220
  else {
@@ -123,6 +229,8 @@ export function unregisterCardFrame(cardId) {
123
229
  frameRegistry.delete(cardId);
124
230
  invalidateCanvasPreviewDocument(cardId);
125
231
  frameProjectionStates.delete(cardId);
232
+ projectionAcknowledgementVersion += 1;
233
+ notifyProjectionAcknowledgementListeners();
126
234
  clearCanvasStructuralProjectionReports(cardId);
127
235
  clearCanvasRenderedInstanceProjectionReports(cardId);
128
236
  clearCanvasTextProjectionReports(cardId);
@@ -153,11 +261,13 @@ function getFrameDocument(iframe) {
153
261
  return null;
154
262
  }
155
263
  }
156
- function markProjectionSent(cardId, iframe, sentRevision) {
264
+ function markProjectionSent(cardId, iframe, sentRevision, canonical = false) {
157
265
  const state = frameProjectionStates.get(cardId);
158
266
  if (!state || state.iframe !== iframe)
159
267
  return;
160
268
  state.sentRevision = Math.max(state.sentRevision, sentRevision);
269
+ if (canonical)
270
+ state.canonicalSentRevision = sentRevision;
161
271
  }
162
272
  /** Records the highest complete projection revision accepted by one frame. */
163
273
  export function recordCanvasProjectionApplied(cardId, appliedRevision) {
@@ -210,23 +320,6 @@ export function projectToAllReadyCards() {
210
320
  return;
211
321
  const { css, revision: rev, instanceOverrides, structuralChanges, textContentChanges, componentOverrides } = computeProjection();
212
322
  for (const [cardId, iframe] of frameRegistry) {
213
- const win = iframe.contentWindow;
214
- if (!win)
215
- continue;
216
- const msg = {
217
- type: "replace-styles",
218
- protocolVersion: PROTOCOL_VERSION,
219
- projectId: PROJECT_ID,
220
- workspaceId: WORKSPACE_ID,
221
- cardId,
222
- css,
223
- revision: rev,
224
- instanceOverrides: [...instanceOverrides],
225
- structuralChanges: [...structuralChanges],
226
- textContentChanges: [...textContentChanges],
227
- componentOverrides: [...componentOverrides],
228
- };
229
- markProjectionSent(cardId, iframe, rev);
230
- win.postMessage(msg, window.location.origin);
323
+ sendProjectionMessage(cardId, iframe, rev, css, instanceOverrides, structuralChanges, textContentChanges, componentOverrides, true);
231
324
  }
232
325
  }
@@ -4,7 +4,7 @@ import { findClosestAnchor, isEligibleNavigation, hasDifferentRoute } from "./li
4
4
  import { installRendererElementSelector } from "./rendererElementSelector.js";
5
5
  import { createFrameThrottle } from "../overlay/frameThrottle.js";
6
6
  import { isNudgeUiDev } from "../runtime/devFlag.js";
7
- import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
7
+ import { getNudgeUiRuntimeConfig, subscribeNudgeUiRuntime } from "../runtime/runtimeConfig.js";
8
8
  let activeRendererBootstrap = null;
9
9
  function ownsRendererBootstrap(owner) {
10
10
  return owner.active && activeRendererBootstrap?.owner === owner;
@@ -20,10 +20,25 @@ function sendFrameReady(owner) {
20
20
  protocolVersion: PROTOCOL_VERSION,
21
21
  url: window.location.href,
22
22
  title: document.title,
23
+ runtime: getNudgeUiRuntimeConfig(),
23
24
  ...identity,
24
25
  };
25
26
  sendToParent(msg);
26
27
  }
28
+ function sendFrameRuntime(owner) {
29
+ if (!ownsRendererBootstrap(owner))
30
+ return;
31
+ const identity = getRendererIdentity();
32
+ if (!identity)
33
+ return;
34
+ const message = {
35
+ type: "frame-runtime",
36
+ protocolVersion: PROTOCOL_VERSION,
37
+ runtime: getNudgeUiRuntimeConfig(),
38
+ ...identity,
39
+ };
40
+ sendToParent(message);
41
+ }
27
42
  function sendFrameMetadata(owner) {
28
43
  if (!ownsRendererBootstrap(owner))
29
44
  return;
@@ -132,6 +147,7 @@ export function bootstrapRenderer() {
132
147
  activeRendererBootstrap = { owner, handle };
133
148
  try {
134
149
  observeFrameMetadata(owner);
150
+ owner.resourceDisposers.push(subscribeNudgeUiRuntime(() => sendFrameRuntime(owner)));
135
151
  const disposeDiagnostics = startRendererProjectionDiagnostics();
136
152
  if (disposeDiagnostics)
137
153
  owner.resourceDisposers.push(disposeDiagnostics);
@@ -139,38 +155,30 @@ export function bootstrapRenderer() {
139
155
  if (disposeSelector)
140
156
  owner.resourceDisposers.push(disposeSelector);
141
157
  installRendererPanProxy(owner);
158
+ let openLinksInCards = false;
142
159
  const onClick = (event) => {
143
160
  if (!ownsRendererBootstrap(owner))
144
161
  return;
145
162
  const anchor = findClosestAnchor(event.target);
146
163
  if (!anchor)
147
164
  return;
148
- if (!isEligibleNavigation(anchor, event)) {
149
- if (isPrimarySelfNavigation(anchor, event)) {
150
- event.preventDefault();
151
- const identity = getRendererIdentity();
152
- if (!identity)
153
- return;
154
- const msg = {
155
- type: "external-navigation",
156
- protocolVersion: PROTOCOL_VERSION,
157
- url: anchor.href,
158
- ...identity,
159
- };
160
- sendToParent(msg);
161
- }
165
+ if (!isEligibleNavigation(anchor, event))
162
166
  return;
163
- }
164
167
  if (!hasDifferentRoute(anchor))
165
168
  return;
166
- event.preventDefault();
167
169
  const identity = getRendererIdentity();
168
170
  if (!identity)
169
171
  return;
172
+ if (openLinksInCards) {
173
+ // Capture on document runs before framework routers, so the card keeps its route.
174
+ event.preventDefault();
175
+ event.stopImmediatePropagation();
176
+ }
170
177
  const msg = {
171
178
  type: "navigation-intent",
172
179
  protocolVersion: PROTOCOL_VERSION,
173
180
  url: anchor.href,
181
+ ...(openLinksInCards ? { openInCard: true } : {}),
174
182
  ...identity,
175
183
  };
176
184
  sendToParent(msg);
@@ -197,6 +205,12 @@ export function bootstrapRenderer() {
197
205
  sendFrameReady(owner);
198
206
  return;
199
207
  }
208
+ const frameIdentity = getRendererIdentity();
209
+ if (frameIdentity && isRendererMessageFor(msg, frameIdentity) && msg.type === "link-target-state") {
210
+ // SAFETY: the identity check proves a protocol message; the discriminator selects this shape.
211
+ openLinksInCards = msg.openInCard === true;
212
+ return;
213
+ }
200
214
  if (msg &&
201
215
  typeof msg === "object" &&
202
216
  msg.type === "replace-styles" &&
@@ -234,18 +248,10 @@ export function bootstrapRenderer() {
234
248
  }
235
249
  return handle;
236
250
  }
237
- function isPrimarySelfNavigation(anchor, event) {
238
- if (event.ctrlKey || event.metaKey || event.shiftKey || event.button !== 0)
239
- return false;
240
- if (anchor.hasAttribute("download"))
241
- return false;
242
- if (anchor.target && anchor.target !== "" && anchor.target !== "_self")
243
- return false;
244
- return anchor.protocol === "http:" || anchor.protocol === "https:";
245
- }
246
251
  function installRendererPanProxy(owner) {
247
252
  let spaceHeld = false;
248
253
  let panning = false;
254
+ let boardGesturesEnabled = false;
249
255
  const panMoveUpdate = createFrameThrottle((point) => {
250
256
  if (!ownsRendererBootstrap(owner))
251
257
  return;
@@ -290,13 +296,20 @@ function installRendererPanProxy(owner) {
290
296
  if (event.data.type === "pan-modifier") {
291
297
  spaceHeld = event.data.spaceHeld;
292
298
  }
299
+ if (event.data.type === "board-gesture-state") {
300
+ boardGesturesEnabled = event.data.enabled;
301
+ if (!boardGesturesEnabled) {
302
+ spaceHeld = false;
303
+ endPan();
304
+ }
305
+ }
293
306
  };
294
307
  window.addEventListener("message", onMessage);
295
308
  owner.listenerRemovers.push(() => window.removeEventListener("message", onMessage));
296
309
  const onKeyDown = (event) => {
297
310
  if (!ownsRendererBootstrap(owner))
298
311
  return;
299
- if (event.code === "Space" && !event.repeat && !isEditableTarget(event.target)) {
312
+ if (boardGesturesEnabled && event.code === "Space" && !event.repeat && !isEditableTarget(event.target)) {
300
313
  spaceHeld = true;
301
314
  if (getRendererIdentity())
302
315
  event.preventDefault();
@@ -342,7 +355,7 @@ function installRendererPanProxy(owner) {
342
355
  const onPointerDown = (event) => {
343
356
  if (!ownsRendererBootstrap(owner))
344
357
  return;
345
- if (!spaceHeld || event.button !== 0 || isEditableTarget(event.target))
358
+ if (!boardGesturesEnabled || !spaceHeld || event.button !== 0 || isEditableTarget(event.target))
346
359
  return;
347
360
  const identity = getRendererIdentity();
348
361
  if (!identity)
@@ -388,7 +401,7 @@ function installRendererPanProxy(owner) {
388
401
  const onWheel = (event) => {
389
402
  if (!ownsRendererBootstrap(owner))
390
403
  return;
391
- if (!event.ctrlKey && !event.metaKey)
404
+ if (!boardGesturesEnabled || (!event.ctrlKey && !event.metaKey))
392
405
  return;
393
406
  const identity = getRendererIdentity();
394
407
  if (!identity)
@@ -1,2 +1,2 @@
1
1
  export declare function buildSelector(el: HTMLElement): string;
2
- export declare function installRendererElementSelector(): () => void;
2
+ export declare function installRendererElementSelector(acceptsClick?: (event: Event) => boolean): () => void;