nudge-ui 0.2.0 → 0.2.1

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 (201) 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 +872 -12
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
  6. package/dist/hosts/astro/clientTransport.js +60 -11
  7. package/dist/hosts/next/sidecar.d.ts +4 -0
  8. package/dist/hosts/next/sidecar.js +49 -6
  9. package/dist/hosts/next/wrapper.js +30 -2
  10. package/dist/hosts/projectBridge.d.ts +20 -0
  11. package/dist/hosts/projectBridge.js +97 -0
  12. package/dist/hosts/static/cli.d.ts +1 -1
  13. package/dist/hosts/static/cli.js +6 -1
  14. package/dist/hosts/static/manifest.d.ts +1 -1
  15. package/dist/hosts/static/manifest.js +1 -1
  16. package/dist/hosts/static/server.js +44 -3
  17. package/dist/hosts/vite/vite.d.ts +5 -1
  18. package/dist/hosts/vite/vite.js +78 -14
  19. package/dist/inspector/agent/McpConnectionDialog.d.ts +2 -2
  20. package/dist/inspector/agent/McpConnectionDialog.js +11 -35
  21. package/dist/inspector/agent/client.d.ts +10 -3
  22. package/dist/inspector/agent/client.js +80 -9
  23. package/dist/inspector/agent/connectionStatus.js +3 -3
  24. package/dist/inspector/agent/httpTransport.d.ts +2 -0
  25. package/dist/inspector/agent/httpTransport.js +37 -17
  26. package/dist/inspector/agent/protocol.d.ts +8 -3
  27. package/dist/inspector/agent/protocol.js +1 -1
  28. package/dist/inspector/agent/verification.d.ts +1 -1
  29. package/dist/inspector/agent/verification.js +69 -12
  30. package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
  31. package/dist/inspector/canvas/CanvasCard.js +181 -91
  32. package/dist/inspector/canvas/CanvasElementOverlay.js +246 -52
  33. package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
  34. package/dist/inspector/canvas/CanvasToolbar.js +24 -0
  35. package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
  36. package/dist/inspector/canvas/CanvasWorkspace.js +392 -73
  37. package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
  38. package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
  39. package/dist/inspector/canvas/canvasResize.d.ts +30 -0
  40. package/dist/inspector/canvas/canvasResize.js +52 -0
  41. package/dist/inspector/canvas/canvasStore.d.ts +30 -6
  42. package/dist/inspector/canvas/canvasStore.js +128 -61
  43. package/dist/inspector/canvas/frameProtocol.d.ts +69 -5
  44. package/dist/inspector/canvas/frameProtocol.js +56 -2
  45. package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -0
  46. package/dist/inspector/canvas/keyboardShortcuts.js +14 -0
  47. package/dist/inspector/canvas/projection.d.ts +7 -0
  48. package/dist/inspector/canvas/projection.js +118 -25
  49. package/dist/inspector/canvas/rendererBootstrap.js +29 -29
  50. package/dist/inspector/canvas/rendererElementSelector.js +341 -30
  51. package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
  52. package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
  53. package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
  54. package/dist/inspector/canvas/sessionStore.d.ts +1 -0
  55. package/dist/inspector/canvas/sessionStore.js +12 -4
  56. package/dist/inspector/canvas/staleChangeDetector.js +16 -14
  57. package/dist/inspector/changes/changesLog.js +31 -1
  58. package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
  59. package/dist/inspector/changes/managedStyleProjection.js +3 -3
  60. package/dist/inspector/changes/structuralTypes.d.ts +4 -0
  61. package/dist/inspector/clientManifest.d.ts +5 -0
  62. package/dist/inspector/clientManifest.js +11 -0
  63. package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
  64. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
  65. package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
  66. package/dist/inspector/index.d.ts +2 -0
  67. package/dist/inspector/index.js +74 -24
  68. package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
  69. package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
  70. package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
  71. package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
  72. package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
  73. package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
  74. package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
  75. package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
  76. package/dist/inspector/inline-text/textEditChange.js +165 -0
  77. package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
  78. package/dist/inspector/inspection/selectionProperty.js +1 -0
  79. package/dist/inspector/overlay/selectedGeometry.js +21 -2
  80. package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
  81. package/dist/inspector/overlay/spacingGestures.js +353 -0
  82. package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
  83. package/dist/inspector/overlay/structuralGestures.js +12 -10
  84. package/dist/inspector/projection/managedStylesheet.js +4 -1
  85. package/dist/inspector/projection/renderedInstance.d.ts +2 -0
  86. package/dist/inspector/projection/renderedInstance.js +5 -1
  87. package/dist/inspector/projection/structuralProjection.d.ts +2 -0
  88. package/dist/inspector/projection/structuralProjection.js +85 -1
  89. package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
  90. package/dist/inspector/projection/textProjection.d.ts +2 -0
  91. package/dist/inspector/projection/textProjection.js +10 -4
  92. package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
  93. package/dist/inspector/prompt/clipboardHandoff.js +3 -3
  94. package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
  95. package/dist/inspector/prompt/generatePrompt.js +19 -2
  96. package/dist/inspector/runtime/editorShell.d.ts +2 -0
  97. package/dist/inspector/runtime/editorShell.js +4 -0
  98. package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
  99. package/dist/inspector/runtime/runtimeConfig.js +4 -0
  100. package/dist/inspector/selection/selectionStore.js +2 -0
  101. package/dist/inspector/settings/SettingsDialog.js +10 -0
  102. package/dist/inspector/shell/AppShell.js +5 -1
  103. package/dist/inspector/shell/ChangesLog.js +8 -4
  104. package/dist/inspector/shell/CopyPromptButton.js +95 -19
  105. package/dist/inspector/shell/EmptyState.js +1 -1
  106. package/dist/inspector/shell/InspectorShell.d.ts +0 -1
  107. package/dist/inspector/shell/InspectorShell.js +37 -38
  108. package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
  109. package/dist/inspector/shell/PeekOriginalButton.js +57 -0
  110. package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
  111. package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
  112. package/dist/inspector/shell/originalPreview.d.ts +9 -0
  113. package/dist/inspector/shell/originalPreview.js +61 -0
  114. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  115. package/dist/inspector/shell/shortcuts.js +15 -0
  116. package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
  117. package/dist/inspector/sketch/SketchChanges.js +72 -0
  118. package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
  119. package/dist/inspector/sketch/SketchEditor.js +402 -0
  120. package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
  121. package/dist/inspector/sketch/SketchEntryButton.js +11 -0
  122. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
  123. package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
  124. package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
  125. package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
  126. package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
  127. package/dist/inspector/sketch/SketchOverlay.js +420 -0
  128. package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
  129. package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
  130. package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
  131. package/dist/inspector/sketch/SketchWorkspace.js +152 -0
  132. package/dist/inspector/sketch/capture.d.ts +55 -0
  133. package/dist/inspector/sketch/capture.js +350 -0
  134. package/dist/inspector/sketch/freehand.d.ts +13 -0
  135. package/dist/inspector/sketch/freehand.js +64 -0
  136. package/dist/inspector/sketch/geometry.d.ts +17 -0
  137. package/dist/inspector/sketch/geometry.js +98 -0
  138. package/dist/inspector/sketch/handoff.d.ts +21 -0
  139. package/dist/inspector/sketch/handoff.js +72 -0
  140. package/dist/inspector/sketch/interaction.d.ts +21 -0
  141. package/dist/inspector/sketch/interaction.js +98 -0
  142. package/dist/inspector/sketch/model.d.ts +104 -0
  143. package/dist/inspector/sketch/model.js +282 -0
  144. package/dist/inspector/sketch/persistence.d.ts +21 -0
  145. package/dist/inspector/sketch/persistence.js +205 -0
  146. package/dist/inspector/sketch/raster.d.ts +22 -0
  147. package/dist/inspector/sketch/raster.js +203 -0
  148. package/dist/inspector/sketch/sketchNote.d.ts +10 -0
  149. package/dist/inspector/sketch/sketchNote.js +30 -0
  150. package/dist/inspector/sketch/store.d.ts +47 -0
  151. package/dist/inspector/sketch/store.js +409 -0
  152. package/dist/inspector/styleEditors/BorderEditor.js +15 -19
  153. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
  154. package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
  155. package/dist/inspector/styleEditors/ColorPicker.js +11 -36
  156. package/dist/inspector/styleEditors/GapField.d.ts +3 -2
  157. package/dist/inspector/styleEditors/GapField.js +2 -2
  158. package/dist/inspector/styleEditors/GridSection.js +1 -1
  159. package/dist/inspector/styleEditors/LayoutSection.js +11 -8
  160. package/dist/inspector/styleEditors/SpacingBox.js +23 -30
  161. package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
  162. package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
  163. package/dist/inspector/styleEditors/layoutValue.js +3 -0
  164. package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
  165. package/dist/inspector/styleEditors/nudgeValue.js +91 -5
  166. package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
  167. package/dist/inspector/styleEditors/stylePresence.js +22 -0
  168. package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
  169. package/dist/inspector/styleEditors/textRelevance.js +35 -0
  170. package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
  171. package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
  172. package/dist/inspector/tokens/TokenField.js +111 -7
  173. package/dist/inspector/tokens/editActions.d.ts +2 -0
  174. package/dist/inspector/tokens/editActions.js +2 -2
  175. package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
  176. package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
  177. package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
  178. package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
  179. package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
  180. package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
  181. package/dist/inspector/tokens/resolution.d.ts +3 -26
  182. package/dist/inspector/tokens/resolution.js +44 -735
  183. package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
  184. package/dist/inspector/tokens/tokenAvailability.js +204 -0
  185. package/dist/inspector/ui/Button.d.ts +1 -1
  186. package/dist/inspector/ui/SideValuesField.js +2 -2
  187. package/dist/inspector/ui/StatusCallout.d.ts +4 -1
  188. package/dist/inspector/ui/StatusCallout.js +2 -2
  189. package/dist/inspector/ui/styles.js +13 -11
  190. package/dist/internal/agent-protocol.d.ts +65 -1
  191. package/dist/internal/agent-protocol.js +183 -3
  192. package/dist/nudge-ui.mjs +261 -57
  193. package/dist/transport/editor.d.ts +28 -0
  194. package/dist/transport/editor.js +132 -0
  195. package/dist/transport/index.d.ts +1 -0
  196. package/dist/transport/index.js +1 -0
  197. package/dist/transport/routes.d.ts +7 -0
  198. package/dist/transport/routes.js +7 -0
  199. package/package.json +3 -1
  200. package/dist/inspector/shell/panelLayout.d.ts +0 -2
  201. package/dist/inspector/shell/panelLayout.js +0 -29
@@ -1,7 +1,13 @@
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
+ // v19 adds spacing drag gestures (hover point, spacing descriptor, start
5
+ // point, and cancelled drag-end) plus renderer-to-controller keyboard shortcut
6
+ // forwarding for Canvas frames.
7
+ // v18 adds parent-to-renderer Alt modifier forwarding for Canvas measurements.
8
+ // v17 adds an idempotent renderer request to open the parent inspector. v16
9
+ // adds controller-owned inline-text intents for Canvas frames. v15 adds
10
+ // renderer-to-controller element deselection for Canvas frames. v14
5
11
  // adds border widths to hover geometry so containment measurements can exclude
6
12
  // the container border. v13 adds source-parent-aware structural moves and
7
13
  // bounded structural failure reasons to the full projection snapshot. v12
@@ -9,7 +15,7 @@ import { isStructuralProjectionReport } from "../projection/structuralProjection
9
15
  // rereading a Canvas iframe until the renderer has applied its revision. v11
10
16
  // added the renderer-hello handshake solicitation for runtimes whose boot
11
17
  // completes after the controller's load-time parent-ready.
12
- export const PROTOCOL_VERSION = 15;
18
+ export const PROTOCOL_VERSION = 19;
13
19
  let rendererIdentity = null;
14
20
  export function setRendererIdentity(identity) {
15
21
  rendererIdentity = identity;
@@ -30,6 +36,21 @@ export function isRendererMessageFor(value, identity) {
30
36
  && message.workspaceId === identity.workspaceId
31
37
  && message.cardId === identity.cardId;
32
38
  }
39
+ export function isKeyboardShortcutCode(value) {
40
+ return value === "Backslash" || value === "KeyV" || value === "KeyH" || value === "KeyP" || value === "KeyS";
41
+ }
42
+ /** Strict JSON-only schema for renderer-originated global keyboard shortcuts. */
43
+ export function isKeyboardShortcutMessage(value, identity) {
44
+ if (!isRendererMessageFor(value, identity) || !isProtocolObject(value))
45
+ return false;
46
+ if (!hasOnlyKeys(value, [
47
+ "type", "protocolVersion", "projectId", "workspaceId", "cardId", "phase", "code",
48
+ ]))
49
+ return false;
50
+ return ownValue(value, "type") === "keyboard-shortcut"
51
+ && (ownValue(value, "phase") === "keydown" || ownValue(value, "phase") === "keyup")
52
+ && isKeyboardShortcutCode(ownValue(value, "code"));
53
+ }
33
54
  /** Strict JSON-only schema for selecting an element from a renderer frame. */
34
55
  export function isElementClickMessage(value, identity) {
35
56
  if (!isRendererMessageFor(value, identity) || !isProtocolObject(value))
@@ -51,6 +72,31 @@ export function isElementClickMessage(value, identity) {
51
72
  && typeof ownValue(value, "component") === "string"
52
73
  && (additive === undefined || typeof additive === "boolean");
53
74
  }
75
+ /** Strict JSON-only schema for starting controller-owned inline text editing. */
76
+ export function isInlineTextIntentMessage(value, identity) {
77
+ if (!isRendererMessageFor(value, identity) || !isProtocolObject(value))
78
+ return false;
79
+ if (!hasOnlyKeys(value, [
80
+ "type", "protocolVersion", "projectId", "workspaceId", "cardId",
81
+ "intent", "cid", "src", "elementId", "point", "clickCount", "emptyProjectionId",
82
+ ]))
83
+ return false;
84
+ const intent = ownValue(value, "intent");
85
+ const point = ownValue(value, "point");
86
+ const clickCount = ownValue(value, "clickCount");
87
+ const emptyProjectionId = ownValue(value, "emptyProjectionId");
88
+ return ownValue(value, "type") === "inline-text-intent"
89
+ && (intent === "pointer-down" || intent === "double-click")
90
+ && typeof ownValue(value, "cid") === "string"
91
+ && typeof ownValue(value, "src") === "string"
92
+ && typeof ownValue(value, "elementId") === "string"
93
+ && isProtocolObject(point)
94
+ && hasOnlyKeys(point, ["x", "y"])
95
+ && typeof ownValue(point, "x") === "number" && Number.isFinite(ownValue(point, "x"))
96
+ && typeof ownValue(point, "y") === "number" && Number.isFinite(ownValue(point, "y"))
97
+ && (clickCount === undefined || (typeof clickCount === "number" && Number.isSafeInteger(clickCount) && clickCount >= 0))
98
+ && (emptyProjectionId === undefined || typeof emptyProjectionId === "string");
99
+ }
54
100
  /** Strict JSON-only schema for renderer diagnostics before the parent records them. */
55
101
  export function isStructuralProjectionReportMessage(value, identity) {
56
102
  if (!isRendererMessageFor(value, identity) || !value || typeof value !== "object")
@@ -137,6 +183,7 @@ function isProtocolObject(value) {
137
183
  function ownValue(value, key) {
138
184
  switch (key) {
139
185
  case "additive": return value.additive;
186
+ case "emptyProjectionId": return value.emptyProjectionId;
140
187
  case "cardId": return value.cardId;
141
188
  case "cid": return value.cid;
142
189
  case "callsiteId": return value.callsiteId;
@@ -144,6 +191,11 @@ function ownValue(value, key) {
144
191
  case "elementId": return value.elementId;
145
192
  case "file": return value.file;
146
193
  case "line": return value.line;
194
+ case "intent": return value.intent;
195
+ case "point": return value.point;
196
+ case "clickCount": return value.clickCount;
197
+ case "x": return value.x;
198
+ case "y": return value.y;
147
199
  case "prop": return value.prop;
148
200
  case "projectId": return value.projectId;
149
201
  case "protocolVersion": return value.protocolVersion;
@@ -153,6 +205,8 @@ function ownValue(value, key) {
153
205
  case "selector": return value.selector;
154
206
  case "src": return value.src;
155
207
  case "component": return value.component;
208
+ case "code": return value.code;
209
+ case "phase": return value.phase;
156
210
  case "value": return value.value;
157
211
  case "workspaceId": return value.workspaceId;
158
212
  }
@@ -0,0 +1,3 @@
1
+ export type CanvasInteractionTool = "move" | "pan" | "sketch";
2
+ export declare function canvasToolForCode(code: string): CanvasInteractionTool | null;
3
+ export declare function canvasToolForEvent(event: KeyboardEvent): CanvasInteractionTool | null;
@@ -0,0 +1,14 @@
1
+ export function canvasToolForCode(code) {
2
+ if (code === "KeyV")
3
+ return "move";
4
+ if (code === "KeyH")
5
+ return "pan";
6
+ if (code === "KeyP")
7
+ return "sketch";
8
+ return null;
9
+ }
10
+ export function canvasToolForEvent(event) {
11
+ if (event.repeat || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
12
+ return null;
13
+ return canvasToolForCode(event.code);
14
+ }
@@ -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);
@@ -145,25 +161,10 @@ export function bootstrapRenderer() {
145
161
  const anchor = findClosestAnchor(event.target);
146
162
  if (!anchor)
147
163
  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
- }
164
+ if (!isEligibleNavigation(anchor, event))
162
165
  return;
163
- }
164
166
  if (!hasDifferentRoute(anchor))
165
167
  return;
166
- event.preventDefault();
167
168
  const identity = getRendererIdentity();
168
169
  if (!identity)
169
170
  return;
@@ -234,18 +235,10 @@ export function bootstrapRenderer() {
234
235
  }
235
236
  return handle;
236
237
  }
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
238
  function installRendererPanProxy(owner) {
247
239
  let spaceHeld = false;
248
240
  let panning = false;
241
+ let boardGesturesEnabled = false;
249
242
  const panMoveUpdate = createFrameThrottle((point) => {
250
243
  if (!ownsRendererBootstrap(owner))
251
244
  return;
@@ -290,13 +283,20 @@ function installRendererPanProxy(owner) {
290
283
  if (event.data.type === "pan-modifier") {
291
284
  spaceHeld = event.data.spaceHeld;
292
285
  }
286
+ if (event.data.type === "board-gesture-state") {
287
+ boardGesturesEnabled = event.data.enabled;
288
+ if (!boardGesturesEnabled) {
289
+ spaceHeld = false;
290
+ endPan();
291
+ }
292
+ }
293
293
  };
294
294
  window.addEventListener("message", onMessage);
295
295
  owner.listenerRemovers.push(() => window.removeEventListener("message", onMessage));
296
296
  const onKeyDown = (event) => {
297
297
  if (!ownsRendererBootstrap(owner))
298
298
  return;
299
- if (event.code === "Space" && !event.repeat && !isEditableTarget(event.target)) {
299
+ if (boardGesturesEnabled && event.code === "Space" && !event.repeat && !isEditableTarget(event.target)) {
300
300
  spaceHeld = true;
301
301
  if (getRendererIdentity())
302
302
  event.preventDefault();
@@ -342,7 +342,7 @@ function installRendererPanProxy(owner) {
342
342
  const onPointerDown = (event) => {
343
343
  if (!ownsRendererBootstrap(owner))
344
344
  return;
345
- if (!spaceHeld || event.button !== 0 || isEditableTarget(event.target))
345
+ if (!boardGesturesEnabled || !spaceHeld || event.button !== 0 || isEditableTarget(event.target))
346
346
  return;
347
347
  const identity = getRendererIdentity();
348
348
  if (!identity)
@@ -388,7 +388,7 @@ function installRendererPanProxy(owner) {
388
388
  const onWheel = (event) => {
389
389
  if (!ownsRendererBootstrap(owner))
390
390
  return;
391
- if (!event.ctrlKey && !event.metaKey)
391
+ if (!boardGesturesEnabled || (!event.ctrlKey && !event.metaKey))
392
392
  return;
393
393
  const identity = getRendererIdentity();
394
394
  if (!identity)