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
@@ -4,7 +4,7 @@ import { beginPreviewAttempt, clearPreviewDiagnostics, getHostPreviewDocument, n
4
4
  import { getWorkspaceChanges } from "../changes/workspaceChanges.js";
5
5
  import { getCanvasPreviewDocument, getRegisteredFrames } from "./projection.js";
6
6
  import { findCanvasFrameBySource, PROJECT_ID, WORKSPACE_ID } from "./projection.js";
7
- import { getCanvasCards } from "./canvasStore.js";
7
+ import { getCanvasCards, getCanvasMode } from "./canvasStore.js";
8
8
  import { isRendererMessageFor } from "./frameProtocol.js";
9
9
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
10
10
  const VERIFICATION_TIMEOUT_MS = 5000;
@@ -79,7 +79,7 @@ function checkTokenDrift(change) {
79
79
  return null;
80
80
  }
81
81
  function getPreviewDocuments() {
82
- const documents = [getHostPreviewDocument()];
82
+ const documents = getCanvasMode() === "canvas" ? [] : [getHostPreviewDocument()];
83
83
  const frames = getRegisteredFrames();
84
84
  for (const card of getCanvasCards()) {
85
85
  if (frames.has(card.id))
@@ -99,15 +99,17 @@ function beginPreviewAttempts() {
99
99
  }
100
100
  function gatherMatchEvidence(changes) {
101
101
  const evidence = new Map();
102
- const hostDocument = getHostPreviewDocument();
103
- for (let i = 0; i < changes.length; i++) {
104
- const change = changes[i];
105
- if (isTokenChange(change))
106
- continue;
107
- if (checkSelectorInDocument(document, change.selector)) {
108
- const routes = evidence.get(i) ?? [];
109
- routes.push(hostDocument);
110
- evidence.set(i, routes);
102
+ if (getCanvasMode() !== "canvas") {
103
+ const hostDocument = getHostPreviewDocument();
104
+ for (let i = 0; i < changes.length; i++) {
105
+ const change = changes[i];
106
+ if (isTokenChange(change))
107
+ continue;
108
+ if (checkSelectorInDocument(document, change.selector)) {
109
+ const routes = evidence.get(i) ?? [];
110
+ routes.push(hostDocument);
111
+ evidence.set(i, routes);
112
+ }
111
113
  }
112
114
  }
113
115
  const frames = getRegisteredFrames();
@@ -131,13 +133,13 @@ function gatherMatchEvidence(changes) {
131
133
  }
132
134
  function applyStaleResults(changes, attempts) {
133
135
  const evidence = gatherMatchEvidence(changes);
134
- const hostAttempt = attempts.get(getHostPreviewDocument().logicalDocument);
136
+ const tokenAttempt = attempts.values().next().value;
135
137
  for (let i = 0; i < changes.length; i++) {
136
138
  const change = changes[i];
137
139
  if (isTokenChange(change)) {
138
140
  const drift = checkTokenDrift(change);
139
- if (drift && hostAttempt)
140
- publishPreviewDiagnostic(hostAttempt, changeKey(change), drift);
141
+ if (drift && tokenAttempt)
142
+ publishPreviewDiagnostic(tokenAttempt, changeKey(change), drift);
141
143
  continue;
142
144
  }
143
145
  const routes = evidence.get(i);
@@ -8,6 +8,8 @@ import { workspaceChangeStore, } from "./workspaceChanges.js";
8
8
  import { beginPreviewAttempt, clearPreviewDiagnostics, getHostPreviewDocument, publishPreviewDiagnostic, } from "./previewDiagnostics.js";
9
9
  import { clearStructuralProjectionReports, pruneStructuralProjectionReports } from "../projection/structuralProjection.js";
10
10
  import { applyHostWorkspaceProjection, compileWorkspaceProjection, } from "../projection/workspaceProjection.js";
11
+ import { isEditorShellDocument } from "../runtime/editorShell.js";
12
+ import { isOriginalPreviewActive, subscribeOriginalPreview, } from "../shell/originalPreview.js";
11
13
  export { isComponentChange, isElementChange, isTextContentChange, isTextContentChangeValue, isPreviewableChange, isTokenChange, } from "./types.js";
12
14
  /**
13
15
  * Deferred delta verification (0044). Only the keys touched by a mutation are
@@ -28,14 +30,28 @@ export function getPendingRules() {
28
30
  return buildManagedStyleRules(getChangesSnapshot());
29
31
  }
30
32
  function reapply(workspace) {
31
- applyHostWorkspaceProjection(compileWorkspaceProjection(workspace));
33
+ // While hold-to-view-original is active the host document intentionally
34
+ // shows the page without inspector changes. Commits still land in the
35
+ // canonical store; the release path re-projects the latest snapshot.
36
+ if (isOriginalPreviewActive())
37
+ return;
38
+ if (!isEditorShellDocument())
39
+ applyHostWorkspaceProjection(compileWorkspaceProjection(workspace));
32
40
  }
33
41
  function flushVerification() {
34
42
  verificationHandle = null;
43
+ // Probing the original preview would report the held-back changes as
44
+ // conflicts. Keep the pending targets; releasing re-verifies them.
45
+ if (isOriginalPreviewActive())
46
+ return;
35
47
  const targets = pendingVerificationTargets;
36
48
  pendingVerificationTargets = new Map();
37
49
  if (targets.size === 0)
38
50
  return;
51
+ // The editor shell has no application document. Renderer diagnostics are
52
+ // owned by renderer projection reports.
53
+ if (isEditorShellDocument())
54
+ return;
39
55
  const current = getChangesSnapshot();
40
56
  const attempt = beginPreviewAttempt(getHostPreviewDocument(), workspaceChangeStore.getSnapshot().revision);
41
57
  if (!attempt)
@@ -93,6 +109,20 @@ function markForVerification(keys, verificationTargets) {
93
109
  if (added)
94
110
  scheduleVerification();
95
111
  }
112
+ /**
113
+ * Releasing hold-to-view-original restores the edited preview; re-verify the
114
+ * surviving set against the restored DOM instead of leaving peek-era state.
115
+ * The retry is forced: a flush that ran during the hold retains its targets
116
+ * without scheduling, so markForVerification alone would see every key
117
+ * already present and schedule nothing.
118
+ */
119
+ subscribeOriginalPreview(() => {
120
+ if (isOriginalPreviewActive())
121
+ return;
122
+ markForVerification(getChangesSnapshot().map(changeKey));
123
+ if (pendingVerificationTargets.size > 0)
124
+ scheduleVerification();
125
+ });
96
126
  /** Append records and report whether canonical workspace state changed. */
97
127
  export function appendChanges(incoming, options = {}) {
98
128
  const result = workspaceChangeStore.commitChangeRecords(incoming);
@@ -3,4 +3,4 @@ import { type ChangeRecord, type PreviewableChangeRecord } from "./types.js";
3
3
  /** The source selector stays canonical; the projection marker is document-local. */
4
4
  export declare function selectorForManagedChange(change: PreviewableChangeRecord): string | null;
5
5
  export declare function buildManagedStyleRules(changes: ChangeRecord[]): StyleRule[];
6
- export declare function verifyManagedStyleProjection(change: PreviewableChangeRecord, selectedElement?: HTMLElement | null): PreviewResult | null;
6
+ export declare function verifyManagedStyleProjection(change: PreviewableChangeRecord, selectedElement?: HTMLElement | null, ownerDocument?: Document): PreviewResult | null;
@@ -63,7 +63,7 @@ export function buildManagedStyleRules(changes) {
63
63
  // emitted afterwards so their additional marker specificity wins.
64
64
  return [...sourceRules.values(), ...instanceRules.values()];
65
65
  }
66
- export function verifyManagedStyleProjection(change, selectedElement) {
66
+ export function verifyManagedStyleProjection(change, selectedElement, ownerDocument = document) {
67
67
  // Deferred verification must use the selection that existed when the
68
68
  // projection was committed. Reading the live selection here can make a
69
69
  // host-document change verify against a later canvas selection (or vice
@@ -75,13 +75,13 @@ export function verifyManagedStyleProjection(change, selectedElement) {
75
75
  // A Canvas-only selection belongs to an iframe. The controller stylesheet
76
76
  // cannot verify it synchronously; leave its result unknown until the frame
77
77
  // has received the canonical projection rather than claiming it is stale.
78
- if (selected && selected.ownerDocument !== document) {
78
+ if (selected && selected.ownerDocument !== ownerDocument) {
79
79
  return null;
80
80
  }
81
81
  let targets = [];
82
82
  try {
83
83
  const selector = selectorForManagedChange(change);
84
- targets = selector ? Array.from(document.querySelectorAll(selector)) : [];
84
+ targets = selector ? Array.from(ownerDocument.querySelectorAll(selector)) : [];
85
85
  }
86
86
  catch {
87
87
  targets = [];
@@ -4,6 +4,10 @@ export interface StructuralDelete {
4
4
  id: string;
5
5
  kind: "delete";
6
6
  target: RenderedInstanceRef;
7
+ /** Hash-independent route where the target was captured. */
8
+ route?: string;
9
+ /** Bounded view identity where the target was captured. */
10
+ state?: string;
7
11
  }
8
12
  /** Controller-owned intent to move one rendered instance. */
9
13
  export interface StructuralMove {
@@ -8,6 +8,11 @@ export interface NudgeUiClientManifest {
8
8
  readonly version: typeof NUDGE_UI_CLIENT_MANIFEST_VERSION;
9
9
  readonly revision: number;
10
10
  readonly runtime: NudgeUiRuntimeConfig;
11
+ /** Project-owned, loopback-only agent bridge started by the dev host. */
12
+ readonly agentBridge?: {
13
+ readonly baseUrl: string;
14
+ readonly autoConnect?: boolean;
15
+ };
11
16
  /** Optional document preparation requested by the host Adapter. */
12
17
  readonly document?: {
13
18
  readonly runtimeIdentity?: "static-html";
@@ -20,6 +20,7 @@ value) {
20
20
  version: NUDGE_UI_CLIENT_MANIFEST_VERSION,
21
21
  revision: value.revision,
22
22
  runtime: normalizeNudgeUiRuntimeConfig(value.runtime),
23
+ ...(isAgentBridge(value.agentBridge) ? { agentBridge: value.agentBridge } : {}),
23
24
  ...(value.document === undefined ? {} : { document: value.document }),
24
25
  ...(value.reload === undefined ? {} : { reload: value.reload }),
25
26
  };
@@ -28,6 +29,16 @@ value) {
28
29
  return null;
29
30
  }
30
31
  }
32
+ function isAgentBridge(value) {
33
+ if (value === undefined)
34
+ return false;
35
+ if (!isRecord(value))
36
+ return false;
37
+ const candidate = value;
38
+ if (typeof candidate.baseUrl !== "string" || candidate.baseUrl.length === 0)
39
+ return false;
40
+ return candidate.autoConnect === undefined || typeof candidate.autoConnect === "boolean";
41
+ }
31
42
  function isDocumentOptions(
32
43
  // Optional manifest capability is parsed before use.
33
44
  // oxlint-disable-next-line anti-slop/no-unknown-parameters
@@ -1,10 +1,10 @@
1
1
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
2
2
  import { copyComponentOverrides, copyRuntimeTarget, getHostRuntimeAdapters, registerHostRuntimeAdapter, replaceHostRuntimeAdapterOverrides, } from "./runtimeBridge.js";
3
3
  const diagnosedAdapters = new WeakSet();
4
- function enabledRuntimeAdapters() {
4
+ function enabledRuntimeAdapters(ownerGlobal) {
5
5
  if (!getNudgeUiRuntimeConfig().capabilities.componentSemantics)
6
6
  return [];
7
- return [...getHostRuntimeAdapters()];
7
+ return [...getHostRuntimeAdapters(ownerGlobal)];
8
8
  }
9
9
  /** Register a framework adapter without coupling semantic resolution to React. */
10
10
  export function registerComponentRuntimeAdapter(adapter) {
@@ -12,7 +12,8 @@ export function registerComponentRuntimeAdapter(adapter) {
12
12
  }
13
13
  export function inspectComponentTargets(element) {
14
14
  const targets = [];
15
- for (const adapter of enabledRuntimeAdapters()) {
15
+ const ownerGlobal = element.ownerDocument.defaultView ?? globalThis;
16
+ for (const adapter of enabledRuntimeAdapters(ownerGlobal)) {
16
17
  let inspected;
17
18
  try {
18
19
  inspected = adapter.inspect(element);
@@ -11,8 +11,8 @@ export declare const NUDGE_UI_HOST_RUNTIME_VERSION = 1;
11
11
  * not accumulate stale implementations.
12
12
  */
13
13
  export declare function registerHostRuntimeAdapter(adapter: ComponentRuntimeAdapter): () => void;
14
- /** Returns the active host runtime Adapters for this browser realm. */
15
- export declare function getHostRuntimeAdapters(): readonly ComponentRuntimeAdapter[];
14
+ /** Returns the active host runtime Adapters for the requested browser realm. */
15
+ export declare function getHostRuntimeAdapters(ownerGlobal?: typeof globalThis): readonly ComponentRuntimeAdapter[];
16
16
  /** Stores the latest projection and sends it to every registered host Adapter. */
17
17
  export declare function replaceHostRuntimeAdapterOverrides(overrides: readonly ComponentOverride[]): void;
18
18
  /**
@@ -42,9 +42,22 @@ export function registerHostRuntimeAdapter(adapter) {
42
42
  }
43
43
  };
44
44
  }
45
- /** Returns the active host runtime Adapters for this browser realm. */
46
- export function getHostRuntimeAdapters() {
47
- return [...registry().adapters.values()];
45
+ /** Returns the active host runtime Adapters for the requested browser realm. */
46
+ export function getHostRuntimeAdapters(ownerGlobal = globalThis) {
47
+ if (ownerGlobal === globalThis)
48
+ return [...registry().adapters.values()];
49
+ const current = ownerGlobal[HOST_RUNTIME_KEY];
50
+ if (current === undefined)
51
+ return [];
52
+ const mapConstructor = Reflect.get(ownerGlobal, "Map");
53
+ if (!mapConstructor
54
+ || typeof current !== "object"
55
+ || current === null
56
+ || current.version !== NUDGE_UI_HOST_RUNTIME_VERSION
57
+ || !(current.adapters instanceof mapConstructor)) {
58
+ throw new Error(`Nudge UI host runtime is incompatible with version ${NUDGE_UI_HOST_RUNTIME_VERSION}.`);
59
+ }
60
+ return [...(current.adapters.values())];
48
61
  }
49
62
  /** Stores the latest projection and sends it to every registered host Adapter. */
50
63
  export function replaceHostRuntimeAdapterOverrides(overrides) {
@@ -1,3 +1,4 @@
1
+ export { resolveNudgeUiClientEntry } from "../transport/editor.js";
1
2
  export declare function bootstrapNudgeUi(inspectorHost: HTMLElement): void;
2
3
  export declare function mountInspector(host: HTMLElement): void;
3
4
  export declare function unmountInspector(): void;
@@ -6,6 +7,7 @@ export { InspectorShell } from "./shell/InspectorShell.js";
6
7
  export { detectFramework } from "./prompt/detectFramework.js";
7
8
  export { installStaticHtmlRuntimeIdentity, isRuntimeGeneratedSource, RUNTIME_ELEMENT_CID_PREFIX, RUNTIME_UNKNOWN_SOURCE_PREFIX, } from "./runtime/staticHtmlRuntimeIdentity.js";
8
9
  export { isCanvasRenderer } from "./canvas/roleDetection.js";
10
+ export { createNudgeUiEditorUrl, hasNudgeUiDirectTabIntent, isNudgeUiDirectUrl, readNudgeUiEditorTarget, rememberNudgeUiDirectTabIntent, } from "../transport/editor.js";
9
11
  export { NUDGE_UI_INSPECTION_VERSION, inspectElement, installInspectionBridge } from "./inspection/bridge.js";
10
12
  export type { NudgeUiInspectionBridge, ElementInspection, InspectElementOptions, InspectionCatalogEntry, InspectionControl } from "./inspection/bridge.js";
11
13
  export { beginInlineTextEdit, cancelInlineTextEdit, disposeInlineTextEdit, getInlineTextDiagnostic, getInlineTextDiagnostics, getInlineTextSession, inlineTextEditor, isInlineTextEditingActive, subscribeInlineTextDiagnostics, resolveTextBinding, useInlineTextSession, } from "./inline-text/inlineTextEditor.js";
@@ -1,11 +1,12 @@
1
1
  import { createElement } from "react";
2
2
  import { createRoot } from "react-dom/client";
3
- import { InspectorShell, toggleInspector, setInspectorOpen, setInspectorHost } from "./shell/InspectorShell.js";
3
+ import { InspectorShell, toggleInspector, setInspectorOpen } from "./shell/InspectorShell.js";
4
4
  import { setSelectedElement } from "./selection/selectionStore.js";
5
5
  import { removeManagedSheet } from "./projection/managedStylesheet.js";
6
- import { isInspectorToggleShortcut } from "./shell/shortcuts.js";
7
- import { clearInspectorLayout } from "./shell/panelLayout.js";
6
+ import { isInspectorToggleShortcut, isOriginalPreviewShortcut, SEND_PROMPT_HOTKEY_EVENT, } from "./shell/shortcuts.js";
8
7
  import { isCanvasRenderer } from "./canvas/roleDetection.js";
8
+ import { isKeyboardShortcutMessage } from "./canvas/frameProtocol.js";
9
+ import { subscribeCanvasRendererMessages } from "./canvas/rendererMessageRouter.js";
9
10
  import { bootstrapRenderer } from "./canvas/rendererBootstrap.js";
10
11
  import { hydrateSession, enableAutoSave, scheduleAutoSave, scheduleCanvasSave, setRestoreCount, } from "./canvas/sessionStore.js";
11
12
  import { subscribeChanges, getChangesList } from "./changes/changesLog.js";
@@ -15,14 +16,18 @@ import { cancelStaleDetection, startStaleDetection } from "./canvas/staleChangeD
15
16
  import { LockedWorkspaceNotice } from "./canvas/LockedWorkspaceNotice.js";
16
17
  import { AppShell } from "./shell/AppShell.js";
17
18
  import { installInspectionBridge } from "./inspection/bridge.js";
18
- import { disposeBrowserCssInspection } from "./inspection/browserCssInspectionRegistry.js";
19
- import { releaseDocumentProjection } from "./projection/structuralProjection.js";
19
+ import { releaseDocumentProjection, subscribeStructuralChanges } from "./projection/structuralProjection.js";
20
20
  import { cancelInlineTextEdit } from "./inline-text/inlineTextEditor.js";
21
21
  import { configureNudgeUiRuntime, getNudgeUiRuntimeConfig, isDemoRuntime } from "./runtime/runtimeConfig.js";
22
22
  import { setCanvasMode } from "./canvas/canvasStore.js";
23
23
  import { isNudgeUiDev, setNudgeUiHostDevFlag } from "./runtime/devFlag.js";
24
- import { clearClipboardHandoff, startClipboardHandoffController, subscribeClipboardHandoff, } from "./prompt/clipboardHandoff.js";
24
+ import { clearClipboardHandoff, subscribeClipboardHandoff, } from "./prompt/clipboardHandoff.js";
25
25
  import { createWorkspace, InspectorSessionProvider, } from "./session/index.js";
26
+ import { clearSketchClipboardHandoff } from "./sketch/handoff.js";
27
+ import { cancelSketchInteraction, isSketchInteractionActive } from "./sketch/interaction.js";
28
+ import { setOriginalPreviewActive } from "./shell/originalPreview.js";
29
+ import { initializeSketchStore } from "./sketch/store.js";
30
+ export { resolveNudgeUiClientEntry } from "../transport/editor.js";
26
31
  let hostElement = null;
27
32
  let reactRoot = null;
28
33
  let lockedRoot = null;
@@ -33,11 +38,47 @@ let beforeUnloadAttached = false;
33
38
  let persistenceSubscribed = false;
34
39
  let unsubscribeOwnership = null;
35
40
  let removeInspectionBridge = null;
36
- let stopClipboardHandoffController = null;
41
+ let keyboardPeekHeld = false;
37
42
  function onKeydown(e) {
43
+ if (isSketchInteractionActive())
44
+ return;
38
45
  if (isInspectorToggleShortcut(e)) {
39
46
  toggleInspector();
40
47
  e.preventDefault();
48
+ return;
49
+ }
50
+ if (isOriginalPreviewShortcut(e)) {
51
+ keyboardPeekHeld = true;
52
+ setOriginalPreviewActive(true);
53
+ e.preventDefault();
54
+ }
55
+ }
56
+ function onKeyup(e) {
57
+ // Release only a keyboard-started peek so a button-held preview survives
58
+ // unrelated Backslash keyups (e.g. the panel-toggle shortcuts pressed
59
+ // mid-hold). A guarded keydown never sets the flag, so this stays a no-op
60
+ // for keyups without a matching hold.
61
+ if (e.code === "Backslash" && keyboardPeekHeld) {
62
+ keyboardPeekHeld = false;
63
+ setOriginalPreviewActive(false);
64
+ }
65
+ }
66
+ function onRendererKeyboardShortcut(message, identity) {
67
+ if (!isKeyboardShortcutMessage(message, identity))
68
+ return;
69
+ if (message.code === "KeyS" && message.phase === "keydown") {
70
+ window.dispatchEvent(new Event(SEND_PROMPT_HOTKEY_EVENT));
71
+ return;
72
+ }
73
+ if (message.code !== "Backslash")
74
+ return;
75
+ if (message.phase === "keydown") {
76
+ keyboardPeekHeld = true;
77
+ setOriginalPreviewActive(true);
78
+ }
79
+ else if (keyboardPeekHeld) {
80
+ keyboardPeekHeld = false;
81
+ setOriginalPreviewActive(false);
41
82
  }
42
83
  }
43
84
  function disposeInspectorSession() {
@@ -57,11 +98,15 @@ export function bootstrapNudgeUi(inspectorHost) {
57
98
  // for its own document. The bundle mode name alone must never flip it,
58
99
  // so every build without both opt-ins stays governed by ADR-0002.
59
100
  setNudgeUiHostDevFlag(true);
60
- // Public demo builds intentionally expose only the static inspector. The
61
- // Canvas workspace, write lease, persistence, and agent bridge stay out
62
- // of the embedded frame.
63
- setCanvasMode("inspect");
101
+ if (runtimeConfig.capabilities.canvas && isCanvasRenderer()) {
102
+ rendererBootstrapHandle = bootstrapRenderer() ?? null;
103
+ return;
104
+ }
105
+ // The public demo shares the iframe editor while keeping the write lease,
106
+ // persistence, and agent bridge disabled.
107
+ setCanvasMode("canvas");
64
108
  mountInspector(inspectorHost);
109
+ setInspectorOpen(false);
65
110
  return;
66
111
  }
67
112
  if (!runtimeConfig.capabilities.canvas) {
@@ -94,6 +139,7 @@ function startController(inspectorHost) {
94
139
  lockedRoot.unmount();
95
140
  lockedRoot = null;
96
141
  }
142
+ void initializeSketchStore(getNudgeUiRuntimeConfig().projectId).catch(() => undefined);
97
143
  unsubscribeOwnership?.();
98
144
  unsubscribeOwnership = subscribeOwnership((hasLease) => {
99
145
  if (!hasLease)
@@ -107,9 +153,12 @@ function startController(inspectorHost) {
107
153
  startStaleDetection(restored);
108
154
  }
109
155
  }
156
+ if (getNudgeUiRuntimeConfig().capabilities.canvas) {
157
+ // Normal controllers always host the iframe workspace. Hydration runs
158
+ // first so this mode change cannot discard cards, camera, or edits.
159
+ setCanvasMode("canvas");
160
+ }
110
161
  mountInspector(inspectorHost);
111
- stopClipboardHandoffController?.();
112
- stopClipboardHandoffController = startClipboardHandoffController(document);
113
162
  enableAutoSave();
114
163
  if (!beforeUnloadAttached) {
115
164
  // enableAutoSave registers its flush listener first. Release the lease only
@@ -120,14 +169,13 @@ function startController(inspectorHost) {
120
169
  }
121
170
  if (!persistenceSubscribed) {
122
171
  subscribeChanges(() => scheduleAutoSave());
172
+ subscribeStructuralChanges(() => scheduleAutoSave());
123
173
  subscribeClipboardHandoff(() => scheduleAutoSave());
124
174
  subscribeCanvas(() => scheduleCanvasSave());
125
175
  persistenceSubscribed = true;
126
176
  }
127
177
  }
128
178
  function mountLockedNotice(host) {
129
- stopClipboardHandoffController?.();
130
- stopClipboardHandoffController = null;
131
179
  if (reactRoot) {
132
180
  reactRoot.unmount();
133
181
  reactRoot = null;
@@ -139,8 +187,9 @@ function mountLockedNotice(host) {
139
187
  cancelStaleDetection();
140
188
  setInspectorOpen(false);
141
189
  cancelInlineTextEdit();
190
+ cancelSketchInteraction();
191
+ clearSketchClipboardHandoff();
142
192
  setSelectedElement(null);
143
- clearInspectorLayout();
144
193
  removeManagedSheet();
145
194
  releaseDocumentProjection(document);
146
195
  hostElement = null;
@@ -178,21 +227,20 @@ export function mountInspector(host) {
178
227
  if (!reactRoot) {
179
228
  reactRoot = createRoot(shadow);
180
229
  inspectorSession = workspace.createInspectorSession(host);
181
- const documentSession = inspectorSession.createDocumentSession(document);
182
- documentSession.registerCleanup(() => disposeBrowserCssInspection(document));
183
- documentSession.registerCleanup(() => releaseDocumentProjection(document));
184
230
  inspectorSession.registerCleanup(() => window.removeEventListener("keydown", onKeydown));
185
- setInspectorHost(host);
186
- reactRoot.render(createElement(InspectorSessionProvider, { inspector: inspectorSession, documentSession }, createElement(demo ? InspectorShell : AppShell)));
231
+ inspectorSession.registerCleanup(() => window.removeEventListener("keyup", onKeyup));
232
+ inspectorSession.registerCleanup(subscribeCanvasRendererMessages(({ identity, message }) => {
233
+ onRendererKeyboardShortcut(message, identity);
234
+ }));
235
+ reactRoot.render(createElement(InspectorSessionProvider, { inspector: inspectorSession, documentSession: null }, createElement(AppShell)));
187
236
  window.addEventListener("keydown", onKeydown);
237
+ window.addEventListener("keyup", onKeyup);
188
238
  }
189
239
  setInspectorOpen(true);
190
240
  }
191
241
  export function unmountInspector() {
192
242
  rendererBootstrapHandle?.teardown();
193
243
  rendererBootstrapHandle = null;
194
- stopClipboardHandoffController?.();
195
- stopClipboardHandoffController = null;
196
244
  unsubscribeOwnership?.();
197
245
  unsubscribeOwnership = null;
198
246
  if (lockedRoot) {
@@ -201,6 +249,7 @@ export function unmountInspector() {
201
249
  }
202
250
  cancelStaleDetection();
203
251
  cancelInlineTextEdit();
252
+ cancelSketchInteraction();
204
253
  setSelectedElement(null);
205
254
  if (reactRoot) {
206
255
  reactRoot.unmount();
@@ -215,8 +264,8 @@ export function unmountInspector() {
215
264
  // a session (gestures, workspace projection) cannot leak across unmount.
216
265
  releaseDocumentProjection(document);
217
266
  clearClipboardHandoff();
267
+ clearSketchClipboardHandoff();
218
268
  removeManagedSheet();
219
- clearInspectorLayout();
220
269
  removeInspectionBridge?.();
221
270
  removeInspectionBridge = null;
222
271
  hostElement = null;
@@ -227,6 +276,7 @@ export { InspectorShell } from "./shell/InspectorShell.js";
227
276
  export { detectFramework } from "./prompt/detectFramework.js";
228
277
  export { installStaticHtmlRuntimeIdentity, isRuntimeGeneratedSource, RUNTIME_ELEMENT_CID_PREFIX, RUNTIME_UNKNOWN_SOURCE_PREFIX, } from "./runtime/staticHtmlRuntimeIdentity.js";
229
278
  export { isCanvasRenderer } from "./canvas/roleDetection.js";
279
+ export { createNudgeUiEditorUrl, hasNudgeUiDirectTabIntent, isNudgeUiDirectUrl, readNudgeUiEditorTarget, rememberNudgeUiDirectTabIntent, } from "../transport/editor.js";
230
280
  export { NUDGE_UI_INSPECTION_VERSION, inspectElement, installInspectionBridge } from "./inspection/bridge.js";
231
281
  export { beginInlineTextEdit, cancelInlineTextEdit, disposeInlineTextEdit, getInlineTextDiagnostic, getInlineTextDiagnostics, getInlineTextSession, inlineTextEditor, isInlineTextEditingActive, subscribeInlineTextDiagnostics, resolveTextBinding, useInlineTextSession, } from "./inline-text/inlineTextEditor.js";
232
282
  export { projectInspectorValues, projectionSides } from "./spacing/projection.js";
@@ -0,0 +1,10 @@
1
+ import type { InlineTextDiagnostic } from "./inlineTextEditor.js";
2
+ export declare function recordInlineTextDiagnostic(diagnostic: InlineTextDiagnostic, ownerDocument?: Document, target?: Element): void;
3
+ export declare function getInlineTextDiagnostics(): readonly InlineTextDiagnostic[];
4
+ export declare function getInlineTextDiagnostic(): InlineTextDiagnostic | null;
5
+ export declare function subscribeInlineTextDiagnostics(listener: () => void): () => void;
6
+ export declare function useInlineTextDiagnostic(): InlineTextDiagnostic | null;
7
+ /** Clear the current diagnostic, optionally only when it belongs to a frame. */
8
+ export declare function clearInlineTextDiagnostics(ownerDocument?: Document): void;
9
+ /** Keep feedback for the target that was just selected, but clear stale feedback. */
10
+ export declare function clearInlineTextDiagnosticsForSelection(selectedElements: readonly HTMLElement[]): void;
@@ -0,0 +1,65 @@
1
+ import { useSyncExternalStore } from "react";
2
+ const listeners = new Set();
3
+ let diagnostics = [];
4
+ let revision = 0;
5
+ function notify() {
6
+ revision += 1;
7
+ for (const listener of listeners)
8
+ listener();
9
+ }
10
+ function sameDiagnostic(left, right) {
11
+ return left.kind === right.kind
12
+ && left.status === right.status
13
+ && left.reason === right.reason
14
+ && left.before === right.before
15
+ && left.after === right.after;
16
+ }
17
+ export function recordInlineTextDiagnostic(diagnostic, ownerDocument, target) {
18
+ const resolvedDocument = ownerDocument ?? target?.ownerDocument;
19
+ const previous = diagnostics.at(-1);
20
+ if (previous
21
+ && previous.ownerDocument === resolvedDocument
22
+ && previous.target === target
23
+ && sameDiagnostic(previous.diagnostic, diagnostic)) {
24
+ return;
25
+ }
26
+ diagnostics = [
27
+ ...diagnostics.slice(-31),
28
+ { diagnostic, ownerDocument: resolvedDocument, target },
29
+ ];
30
+ notify();
31
+ }
32
+ export function getInlineTextDiagnostics() {
33
+ return diagnostics.map(({ diagnostic }) => diagnostic);
34
+ }
35
+ export function getInlineTextDiagnostic() {
36
+ return diagnostics.at(-1)?.diagnostic ?? null;
37
+ }
38
+ export function subscribeInlineTextDiagnostics(listener) {
39
+ listeners.add(listener);
40
+ return () => listeners.delete(listener);
41
+ }
42
+ export function useInlineTextDiagnostic() {
43
+ useSyncExternalStore(subscribeInlineTextDiagnostics, () => revision, () => revision);
44
+ return getInlineTextDiagnostic();
45
+ }
46
+ /** Clear the current diagnostic, optionally only when it belongs to a frame. */
47
+ export function clearInlineTextDiagnostics(ownerDocument) {
48
+ const current = diagnostics.at(-1);
49
+ if (!current)
50
+ return;
51
+ if (ownerDocument && current.ownerDocument !== ownerDocument)
52
+ return;
53
+ diagnostics = [];
54
+ notify();
55
+ }
56
+ /** Keep feedback for the target that was just selected, but clear stale feedback. */
57
+ export function clearInlineTextDiagnosticsForSelection(selectedElements) {
58
+ const current = diagnostics.at(-1);
59
+ if (!current)
60
+ return;
61
+ const target = current.target;
62
+ if (target && selectedElements.length === 1 && selectedElements[0] === target)
63
+ return;
64
+ clearInlineTextDiagnostics();
65
+ }
@@ -1,4 +1,4 @@
1
- import { type TextBindingChoice, type TextEditBinding, type TextEditRejection } from "../componentSemantics/textBinding.js";
1
+ import { type TextBindingChoice, type TextEditBinding, type TextEditRejection, type TextEditRejectionReason } from "../componentSemantics/textBinding.js";
2
2
  import type { ChangeRecord } from "../changes/types.js";
3
3
  import type { TextProjectionScope } from "./textChangeBoundary.js";
4
4
  export type { TextEditBinding, TextEditRejection } from "../componentSemantics/textBinding.js";
@@ -28,13 +28,11 @@ export type InlineTextInputRejectionReason = "cross-host-range" | "rich-input" |
28
28
  export interface InlineTextDiagnostic {
29
29
  kind: "inline-text";
30
30
  status: "started" | "committed" | "cancelled" | "rejected";
31
- reason: InlineTextSessionEndReason | InlineTextInputRejectionReason;
31
+ reason: InlineTextSessionEndReason | InlineTextInputRejectionReason | TextEditRejectionReason;
32
32
  before: string;
33
33
  after?: string;
34
34
  }
35
- export declare function getInlineTextDiagnostics(): readonly InlineTextDiagnostic[];
36
- export declare function getInlineTextDiagnostic(): InlineTextDiagnostic | null;
37
- export declare function subscribeInlineTextDiagnostics(listener: () => void): () => void;
35
+ export { clearInlineTextDiagnostics, getInlineTextDiagnostic, getInlineTextDiagnostics, subscribeInlineTextDiagnostics, useInlineTextDiagnostic, } from "./inlineTextDiagnostics.js";
38
36
  export declare function getInlineTextSession(): InlineTextSession | null;
39
37
  export declare function useInlineTextSession(): InlineTextSession | null;
40
38
  export declare function isInlineTextEditingActive(): boolean;