nudge-ui 0.2.2 → 0.2.4

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 (244) hide show
  1. package/README.md +164 -46
  2. package/dist/client.mjs +260 -61
  3. package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
  4. package/dist/css/value-semantics/colorSemantics.js +17 -0
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
  6. package/dist/hosts/astro/clientTransport.js +38 -32
  7. package/dist/hosts/astro/integration.js +5 -1
  8. package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
  9. package/dist/hosts/astro/markdownIdentity.js +81 -0
  10. package/dist/hosts/next/manifest.js +1 -1
  11. package/dist/hosts/next/sidecar.js +5 -1
  12. package/dist/hosts/static/manifest.js +1 -1
  13. package/dist/hosts/static/server.js +3 -0
  14. package/dist/hosts/vite/vite.d.ts +3 -0
  15. package/dist/hosts/vite/vite.js +24 -4
  16. package/dist/inspector/agent/McpConnectionDialog.js +13 -19
  17. package/dist/inspector/agent/activity.d.ts +18 -0
  18. package/dist/inspector/agent/activity.js +70 -0
  19. package/dist/inspector/agent/client.d.ts +2 -0
  20. package/dist/inspector/agent/client.js +29 -1
  21. package/dist/inspector/agent/connectionStatus.js +1 -1
  22. package/dist/inspector/agent/httpTransport.js +9 -2
  23. package/dist/inspector/agent/protocol.d.ts +6 -3
  24. package/dist/inspector/agent/protocol.js +1 -1
  25. package/dist/inspector/agent/verification.d.ts +15 -7
  26. package/dist/inspector/agent/verification.js +113 -41
  27. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
  28. package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
  29. package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
  30. package/dist/inspector/canvas/CanvasCard.js +135 -38
  31. package/dist/inspector/canvas/CanvasElementOverlay.js +166 -16
  32. package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
  33. package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
  34. package/dist/inspector/canvas/CanvasToolbar.js +8 -4
  35. package/dist/inspector/canvas/CanvasWorkspace.js +270 -122
  36. package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
  37. package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
  38. package/dist/inspector/canvas/agentPresentation.js +64 -52
  39. package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
  40. package/dist/inspector/canvas/canvasGestures.js +1 -1
  41. package/dist/inspector/canvas/canvasStore.d.ts +46 -82
  42. package/dist/inspector/canvas/canvasStore.js +251 -284
  43. package/dist/inspector/canvas/frameContent.d.ts +21 -0
  44. package/dist/inspector/canvas/frameContent.js +44 -0
  45. package/dist/inspector/canvas/frameProtocol.d.ts +21 -4
  46. package/dist/inspector/canvas/frameProtocol.js +3 -3
  47. package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
  48. package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
  49. package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -1
  50. package/dist/inspector/canvas/keyboardShortcuts.js +4 -4
  51. package/dist/inspector/canvas/layout.d.ts +52 -0
  52. package/dist/inspector/canvas/layout.js +106 -0
  53. package/dist/inspector/canvas/projection.d.ts +15 -5
  54. package/dist/inspector/canvas/projection.js +69 -13
  55. package/dist/inspector/canvas/rendererBootstrap.js +32 -4
  56. package/dist/inspector/canvas/rendererElementSelector.js +47 -6
  57. package/dist/inspector/canvas/rendererStylesheet.js +2 -2
  58. package/dist/inspector/canvas/resizeHandleScale.js +1 -1
  59. package/dist/inspector/canvas/sessionStore.d.ts +11 -26
  60. package/dist/inspector/canvas/sessionStore.js +43 -183
  61. package/dist/inspector/canvas/staleChangeDetector.js +9 -3
  62. package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
  63. package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
  64. package/dist/inspector/canvas/viewStore.d.ts +38 -0
  65. package/dist/inspector/canvas/viewStore.js +104 -0
  66. package/dist/inspector/canvas/wheelPan.d.ts +8 -0
  67. package/dist/inspector/canvas/wheelPan.js +8 -0
  68. package/dist/inspector/changes/changesLog.d.ts +9 -6
  69. package/dist/inspector/changes/changesLog.js +48 -32
  70. package/dist/inspector/changes/codecs.d.ts +1 -0
  71. package/dist/inspector/changes/codecs.js +10 -1
  72. package/dist/inspector/changes/draftChanges.d.ts +58 -0
  73. package/dist/inspector/changes/draftChanges.js +304 -0
  74. package/dist/inspector/changes/editModel.d.ts +10 -0
  75. package/dist/inspector/changes/editModel.js +9 -2
  76. package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
  77. package/dist/inspector/changes/previewDiagnostics.js +11 -25
  78. package/dist/inspector/changes/types.d.ts +7 -0
  79. package/dist/inspector/clientManifest.d.ts +1 -1
  80. package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
  81. package/dist/inspector/comments/CommentsOverlay.js +250 -0
  82. package/dist/inspector/comments/element.d.ts +6 -0
  83. package/dist/inspector/comments/element.js +54 -0
  84. package/dist/inspector/comments/store.d.ts +29 -0
  85. package/dist/inspector/comments/store.js +121 -0
  86. package/dist/inspector/comments/verification.d.ts +5 -0
  87. package/dist/inspector/comments/verification.js +52 -0
  88. package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
  89. package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
  90. package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
  91. package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
  92. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
  93. package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
  94. package/dist/inspector/componentSemantics/types.d.ts +11 -0
  95. package/dist/inspector/drafts/model.d.ts +35 -0
  96. package/dist/inspector/drafts/model.js +45 -0
  97. package/dist/inspector/drafts/store.d.ts +28 -0
  98. package/dist/inspector/drafts/store.js +231 -0
  99. package/dist/inspector/index.js +20 -27
  100. package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
  101. package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
  102. package/dist/inspector/iterations/capture.d.ts +2 -0
  103. package/dist/inspector/iterations/capture.js +174 -0
  104. package/dist/inspector/iterations/client.d.ts +7 -0
  105. package/dist/inspector/iterations/client.js +52 -0
  106. package/dist/inspector/iterations/coordinator.d.ts +15 -0
  107. package/dist/inspector/iterations/coordinator.js +130 -0
  108. package/dist/inspector/iterations/prompt.d.ts +3 -0
  109. package/dist/inspector/iterations/prompt.js +26 -0
  110. package/dist/inspector/overlay/DomNavigator.js +21 -15
  111. package/dist/inspector/overlay/interactionStyles.js +3 -1
  112. package/dist/inspector/overlay/spacingGestures.d.ts +13 -1
  113. package/dist/inspector/overlay/spacingGestures.js +82 -5
  114. package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
  115. package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
  116. package/dist/inspector/projection/renderedInstance.d.ts +17 -3
  117. package/dist/inspector/projection/renderedInstance.js +62 -11
  118. package/dist/inspector/projection/structuralProjection.d.ts +2 -1
  119. package/dist/inspector/projection/structuralProjection.js +21 -17
  120. package/dist/inspector/projection/textProjection.d.ts +2 -1
  121. package/dist/inspector/projection/textProjection.js +64 -15
  122. package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
  123. package/dist/inspector/prompt/clipboardHandoff.d.ts +15 -18
  124. package/dist/inspector/prompt/clipboardHandoff.js +97 -143
  125. package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
  126. package/dist/inspector/prompt/generatePrompt.js +22 -17
  127. package/dist/inspector/runtime/runtimeConfig.d.ts +9 -0
  128. package/dist/inspector/runtime/runtimeConfig.js +24 -0
  129. package/dist/inspector/selection/sourceSite.d.ts +12 -0
  130. package/dist/inspector/selection/sourceSite.js +28 -0
  131. package/dist/inspector/settings/SettingsDialog.js +21 -8
  132. package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
  133. package/dist/inspector/shell/AgentStatusButton.js +36 -0
  134. package/dist/inspector/shell/AppShell.js +4 -2
  135. package/dist/inspector/shell/ChangesLog.js +46 -26
  136. package/dist/inspector/shell/CopyPromptButton.js +184 -93
  137. package/dist/inspector/shell/EmptyState.js +9 -8
  138. package/dist/inspector/shell/InspectorShell.js +13 -21
  139. package/dist/inspector/shell/PeekOriginalButton.js +24 -23
  140. package/dist/inspector/shell/originalPreview.js +5 -5
  141. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  142. package/dist/inspector/shell/shortcuts.js +11 -0
  143. package/dist/inspector/sketch/SketchEditor.js +14 -14
  144. package/dist/inspector/sketch/SketchEntryButton.js +1 -1
  145. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
  146. package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
  147. package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
  148. package/dist/inspector/sketch/SketchOverlay.js +7 -7
  149. package/dist/inspector/sketch/SketchWorkspace.js +18 -12
  150. package/dist/inspector/sketch/interaction.d.ts +1 -0
  151. package/dist/inspector/sketch/interaction.js +8 -4
  152. package/dist/inspector/sketch/model.d.ts +5 -0
  153. package/dist/inspector/sketch/model.js +6 -2
  154. package/dist/inspector/sketch/raster.d.ts +1 -2
  155. package/dist/inspector/sketch/raster.js +68 -4
  156. package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
  157. package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
  158. package/dist/inspector/styleEditors/BorderEditor.js +49 -78
  159. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
  160. package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
  161. package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
  162. package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
  163. package/dist/inspector/styleEditors/ColorPicker.js +10 -4
  164. package/dist/inspector/styleEditors/GapField.js +3 -5
  165. package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
  166. package/dist/inspector/styleEditors/GridChildSection.js +18 -9
  167. package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
  168. package/dist/inspector/styleEditors/GridPicker.js +8 -2
  169. package/dist/inspector/styleEditors/GridSection.js +1 -1
  170. package/dist/inspector/styleEditors/GridValueField.js +19 -14
  171. package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
  172. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
  173. package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
  174. package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
  175. package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
  176. package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
  177. package/dist/inspector/styleEditors/LayoutSection.js +32 -11
  178. package/dist/inspector/styleEditors/SpacingBox.js +7 -5
  179. package/dist/inspector/styleEditors/Typography.js +11 -3
  180. package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
  181. package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
  182. package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
  183. package/dist/inspector/styleEditors/gradientValue.js +114 -0
  184. package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
  185. package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
  186. package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
  187. package/dist/inspector/styleEditors/layoutValue.js +0 -42
  188. package/dist/inspector/styleEditors/nudgeValue.d.ts +2 -2
  189. package/dist/inspector/styleEditors/nudgeValue.js +36 -8
  190. package/dist/inspector/tokens/TokenField.d.ts +7 -0
  191. package/dist/inspector/tokens/TokenField.js +35 -25
  192. package/dist/inspector/tokens/TokenLabel.js +5 -14
  193. package/dist/inspector/tokens/editActions.d.ts +1 -1
  194. package/dist/inspector/tokens/editActions.js +7 -1
  195. package/dist/inspector/ui/AtRuleContext.js +4 -6
  196. package/dist/inspector/ui/Button.d.ts +6 -2
  197. package/dist/inspector/ui/Button.js +4 -3
  198. package/dist/inspector/ui/ControlSurface.d.ts +2 -2
  199. package/dist/inspector/ui/ControlSurface.js +4 -3
  200. package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
  201. package/dist/inspector/ui/InlineStyleWarning.js +13 -0
  202. package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
  203. package/dist/inspector/ui/InspectorPopover.js +4 -5
  204. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  205. package/dist/inspector/ui/PopoverListbox.js +8 -7
  206. package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
  207. package/dist/inspector/ui/SegmentedControl.js +2 -2
  208. package/dist/inspector/ui/Select.d.ts +6 -1
  209. package/dist/inspector/ui/Select.js +7 -11
  210. package/dist/inspector/ui/SideValuesField.js +1 -1
  211. package/dist/inspector/ui/Tooltip.d.ts +26 -0
  212. package/dist/inspector/ui/Tooltip.js +32 -0
  213. package/dist/inspector/ui/classNames.d.ts +2 -0
  214. package/dist/inspector/ui/classNames.js +4 -0
  215. package/dist/inspector/ui/customCursors.d.ts +23 -0
  216. package/dist/inspector/ui/customCursors.js +122 -0
  217. package/dist/inspector/ui/dragCursor.d.ts +0 -2
  218. package/dist/inspector/ui/dragCursor.js +2 -10
  219. package/dist/inspector/ui/portalContainer.d.ts +2 -0
  220. package/dist/inspector/ui/portalContainer.js +6 -0
  221. package/dist/inspector/ui/styles.js +29 -21
  222. package/dist/inspector/workspace/commands.d.ts +9 -0
  223. package/dist/inspector/workspace/commands.js +82 -0
  224. package/dist/inspector/workspace/controller.d.ts +4 -0
  225. package/dist/inspector/workspace/controller.js +66 -0
  226. package/dist/inspector/workspace/handoff.d.ts +42 -0
  227. package/dist/inspector/workspace/handoff.js +115 -0
  228. package/dist/inspector/workspace/sentItems.d.ts +19 -0
  229. package/dist/inspector/workspace/sentItems.js +51 -0
  230. package/dist/inspector/workspace/timeline.d.ts +30 -0
  231. package/dist/inspector/workspace/timeline.js +108 -0
  232. package/dist/internal/agent-protocol.d.ts +19 -2
  233. package/dist/internal/agent-protocol.js +13 -0
  234. package/dist/nudge-ui.mjs +558 -311
  235. package/dist/project/artifacts.d.ts +13 -0
  236. package/dist/project/artifacts.js +253 -0
  237. package/dist/transport/artifacts.d.ts +8 -0
  238. package/dist/transport/artifacts.js +9 -0
  239. package/dist/transport/editor.js +3 -1
  240. package/dist/transport/routes.d.ts +2 -0
  241. package/dist/transport/routes.js +2 -0
  242. package/package.json +7 -3
  243. package/dist/inspector/changes/workspaceChanges.d.ts +0 -47
  244. package/dist/inspector/changes/workspaceChanges.js +0 -265
@@ -0,0 +1,52 @@
1
+ import { getDraftChanges } from "../changes/draftChanges.js";
2
+ import { documentTarget, targetKey } from "../drafts/model.js";
3
+ import { isCanvasProjectionRevisionCurrent, projectDraftToDocument } from "../canvas/projection.js";
4
+ import { commentFingerprint, commentViewport, resolveCommentElement } from "./element.js";
5
+ import { commentRoute, getCommentClearRevision, getComments, removeComment } from "./store.js";
6
+ const documentReads = new WeakMap();
7
+ const readingDocuments = new WeakSet();
8
+ export function isReadingCommentSource(doc) { return readingDocuments.has(doc); }
9
+ /** Serializes clean reads and restores the latest previews even if the read fails. */
10
+ export function readCommentSource(doc, read) {
11
+ const previous = documentReads.get(doc) ?? Promise.resolve();
12
+ const operation = previous.catch(() => undefined).then(async () => {
13
+ readingDocuments.add(doc);
14
+ try {
15
+ const revision = await projectDraftToDocument(doc, { changes: [], structuralChanges: [] });
16
+ if (revision === null || !isCanvasProjectionRevisionCurrent(doc, revision))
17
+ return null;
18
+ return read();
19
+ }
20
+ finally {
21
+ try {
22
+ await projectDraftToDocument(doc, getDraftChanges(targetKey(documentTarget(doc.location.href))));
23
+ // Deliver projection mutation records before accepting another source signal.
24
+ await new Promise((resolve) => setTimeout(resolve, 0));
25
+ }
26
+ finally {
27
+ readingDocuments.delete(doc);
28
+ }
29
+ }
30
+ });
31
+ documentReads.set(doc, operation);
32
+ return operation;
33
+ }
34
+ /** Changed, uniquely located elements resolve after handoff; missing and ambiguous targets remain. */
35
+ export async function reconcileComments(doc) {
36
+ const revision = getCommentClearRevision();
37
+ const sent = getComments().filter((item) => item.handedOff && item.route === commentRoute(doc));
38
+ if (sent.length === 0)
39
+ return;
40
+ const resolved = await readCommentSource(doc, () => sent.filter((item) => {
41
+ if (item.viewport !== commentViewport(doc))
42
+ return false;
43
+ const element = resolveCommentElement(doc, item.target);
44
+ return element !== null && commentFingerprint(element) !== item.baseline;
45
+ }));
46
+ if (revision !== getCommentClearRevision())
47
+ return;
48
+ for (const item of resolved ?? []) {
49
+ if (getComments().some((current) => current.id === item.id && current.note === item.note && current.handedOff))
50
+ removeComment(item.id);
51
+ }
52
+ }
@@ -1,7 +1,12 @@
1
- import type { ComponentOverride, ComponentRuntimeAdapter, EditableComponentTarget, RuntimeComponentTarget } from "./types.js";
1
+ import type { ComponentOverride, ComponentRootInvocation, ComponentRuntimeAdapter, EditableComponentTarget, RuntimeComponentTarget } from "./types.js";
2
2
  /** Register a framework adapter without coupling semantic resolution to React. */
3
3
  export declare function registerComponentRuntimeAdapter(adapter: ComponentRuntimeAdapter): () => void;
4
4
  export declare function inspectComponentTargets(element: HTMLElement): RuntimeComponentTarget[];
5
+ /**
6
+ * Returns the invocations, nearest first, whose entire rendered output is this
7
+ * element. Null means no enabled runtime could read the element's ancestry.
8
+ */
9
+ export declare function inspectRootInvocations(element: HTMLElement): ComponentRootInvocation[] | null;
5
10
  /**
6
11
  * Resolve multiplicity through the owning runtime Adapter. The inspector does
7
12
  * not infer it from DOM order or retain a React fiber as durable identity.
@@ -1,5 +1,5 @@
1
1
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
2
- import { copyComponentOverrides, copyRuntimeTarget, getHostRuntimeAdapters, registerHostRuntimeAdapter, replaceHostRuntimeAdapterOverrides, } from "./runtimeBridge.js";
2
+ import { copyComponentOverrides, copyRootInvocations, copyRuntimeTarget, getHostRuntimeAdapters, registerHostRuntimeAdapter, replaceHostRuntimeAdapterOverrides, } from "./runtimeBridge.js";
3
3
  const diagnosedAdapters = new WeakSet();
4
4
  function enabledRuntimeAdapters(ownerGlobal) {
5
5
  if (!getNudgeUiRuntimeConfig().capabilities.componentSemantics)
@@ -42,6 +42,28 @@ export function inspectComponentTargets(element) {
42
42
  }
43
43
  return targets;
44
44
  }
45
+ /**
46
+ * Returns the invocations, nearest first, whose entire rendered output is this
47
+ * element. Null means no enabled runtime could read the element's ancestry.
48
+ */
49
+ export function inspectRootInvocations(element) {
50
+ let invocations = null;
51
+ const ownerGlobal = element.ownerDocument.defaultView ?? globalThis;
52
+ for (const adapter of enabledRuntimeAdapters(ownerGlobal)) {
53
+ if (!adapter.rootInvocations)
54
+ continue;
55
+ try {
56
+ const inspected = adapter.rootInvocations(element);
57
+ if (inspected === null || inspected === undefined)
58
+ continue;
59
+ invocations = [...(invocations ?? []), ...copyRootInvocations(inspected)];
60
+ }
61
+ catch (error) {
62
+ diagnoseAdapter(adapter, error instanceof Error ? error.message : String(error));
63
+ }
64
+ }
65
+ return invocations;
66
+ }
45
67
  function diagnoseAdapter(adapter, detail) {
46
68
  if (diagnosedAdapters.has(adapter))
47
69
  return;
@@ -1,10 +1,11 @@
1
1
  import { type ReactElement } from "react";
2
- import type { ComponentInvocationMeta, ComponentOverride, ComponentRuntimeAdapter, RuntimeComponentTarget } from "./types.js";
2
+ import type { ComponentInvocationMeta, ComponentOverride, ComponentRootInvocation, ComponentRuntimeAdapter, RuntimeComponentTarget } from "./types.js";
3
3
  /** Returns null when this runtime has no mounted boundary for the callsite. */
4
4
  export declare function getReactCallsiteMultiplicity(callsiteId: string): number | null;
5
5
  /** Test/runtime teardown hook; canonical changes never depend on this state. */
6
6
  export declare function resetReactComponentRuntime(): void;
7
7
  export declare function instrumentReactComponent(element: ReactElement<Record<string, unknown>>, meta: ComponentInvocationMeta): ReactElement;
8
8
  export declare function inspectReactComponentTargets(element: HTMLElement): RuntimeComponentTarget[];
9
+ export declare function inspectReactRootInvocations(element: HTMLElement): ComponentRootInvocation[] | null;
9
10
  export declare function replaceReactComponentOverrides(overrides: ComponentOverride[]): void;
10
11
  export declare const reactComponentRuntimeAdapter: ComponentRuntimeAdapter;
@@ -4,6 +4,9 @@ import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
4
4
  const BOUNDARY_MARKER = Symbol.for("nudge-ui.react-component-boundary");
5
5
  const EMPTY_OVERRIDE = Object.freeze({});
6
6
  const REACT_MAJOR_VERSION = Number.parseInt(reactVersion, 10);
7
+ /** React work tags for host output that has no string element type. */
8
+ const HOST_PORTAL_TAG = 4;
9
+ const HOST_TEXT_TAG = 6;
7
10
  let overridesByCallsite = new Map();
8
11
  const listeners = new Set();
9
12
  /**
@@ -161,6 +164,40 @@ export function inspectReactComponentTargets(element) {
161
164
  }
162
165
  return targets;
163
166
  }
167
+ function isHostOutput(fiber) {
168
+ return typeof fiber.type === "string" || fiber.tag === HOST_TEXT_TAG || fiber.tag === HOST_PORTAL_TAG;
169
+ }
170
+ /** Counts top-level host outputs below a fiber, stopping once there are two. */
171
+ function hostOutputCount(fiber) {
172
+ let count = 0;
173
+ const visit = (parent) => {
174
+ for (let child = parent.child ?? null; child && count < 2; child = child.sibling ?? null) {
175
+ if (isHostOutput(child))
176
+ count += 1;
177
+ else
178
+ visit(child);
179
+ }
180
+ };
181
+ visit(fiber);
182
+ return count;
183
+ }
184
+ export function inspectReactRootInvocations(element) {
185
+ if (!getNudgeUiRuntimeConfig().capabilities.componentSemantics)
186
+ return null;
187
+ const host = findFiber(element);
188
+ if (!host)
189
+ return null;
190
+ const invocations = [];
191
+ for (let fiber = host.return ?? null; fiber && !isHostOutput(fiber); fiber = fiber.return ?? null) {
192
+ if (!isBoundaryType(fiber.type) || !fiber.memoizedProps?.meta)
193
+ continue;
194
+ if (hostOutputCount(fiber) !== 1)
195
+ break;
196
+ const { callsiteId, componentName } = fiber.memoizedProps.meta;
197
+ invocations.push({ callsiteId, componentName });
198
+ }
199
+ return invocations;
200
+ }
164
201
  export function replaceReactComponentOverrides(overrides) {
165
202
  if (!getNudgeUiRuntimeConfig().capabilities.componentSemantics)
166
203
  return;
@@ -182,5 +219,6 @@ export const reactComponentRuntimeAdapter = {
182
219
  inspect: inspectReactComponentTargets,
183
220
  replaceOverrides: replaceReactComponentOverrides,
184
221
  getCallsiteMultiplicity: getReactCallsiteMultiplicity,
222
+ rootInvocations: inspectReactRootInvocations,
185
223
  };
186
224
  registerHostRuntimeAdapter(reactComponentRuntimeAdapter);
@@ -1,5 +1,5 @@
1
- import type { ComponentOverride, ComponentRuntimeAdapter, RuntimeComponentTarget } from "./types.js";
2
- export type { ComponentFramework, ComponentInvocationMeta, ComponentOverride, ComponentRuntimeAdapter, RuntimeComponentTarget, } from "./types.js";
1
+ import type { ComponentOverride, ComponentRootInvocation, ComponentRuntimeAdapter, RuntimeComponentTarget } from "./types.js";
2
+ export type { ComponentFramework, ComponentInvocationMeta, ComponentOverride, ComponentRootInvocation, ComponentRuntimeAdapter, RuntimeComponentTarget, } from "./types.js";
3
3
  /** Version of the page-global host runtime Adapter Interface. */
4
4
  export declare const NUDGE_UI_HOST_RUNTIME_VERSION = 1;
5
5
  /**
@@ -21,5 +21,7 @@ export declare function replaceHostRuntimeAdapterOverrides(overrides: readonly C
21
21
  * the host/client seam.
22
22
  */
23
23
  export declare function copyRuntimeTarget(target: RuntimeComponentTarget): RuntimeComponentTarget;
24
+ /** Copies a host Adapter's root invocations into plain protocol data. */
25
+ export declare function copyRootInvocations(invocations: readonly ComponentRootInvocation[]): ComponentRootInvocation[];
24
26
  /** Copies override commands before dispatching them into a host Adapter. */
25
27
  export declare function copyComponentOverrides(overrides: readonly ComponentOverride[]): ComponentOverride[];
@@ -104,6 +104,23 @@ export function copyRuntimeTarget(target) {
104
104
  ...(target.mountedCount === undefined ? {} : { mountedCount: target.mountedCount }),
105
105
  };
106
106
  }
107
+ /** Copies a host Adapter's root invocations into plain protocol data. */
108
+ export function copyRootInvocations(invocations) {
109
+ if (!Array.isArray(invocations)) {
110
+ throw new TypeError("Nudge UI received invalid root invocations from a host Adapter.");
111
+ }
112
+ return invocations.map((invocation) => {
113
+ if (invocation === null
114
+ || typeof invocation !== "object"
115
+ || typeof invocation.callsiteId !== "string"
116
+ || invocation.callsiteId.length === 0
117
+ || typeof invocation.componentName !== "string"
118
+ || invocation.componentName.length === 0) {
119
+ throw new TypeError("Nudge UI received an invalid root invocation from a host Adapter.");
120
+ }
121
+ return { callsiteId: invocation.callsiteId, componentName: invocation.componentName };
122
+ });
123
+ }
107
124
  /** Copies override commands before dispatching them into a host Adapter. */
108
125
  export function copyComponentOverrides(overrides) {
109
126
  return overrides.map((override) => {
@@ -61,6 +61,11 @@ export interface RuntimeComponentTarget {
61
61
  /** Adapter-reported mounted invocation count for this callsite. */
62
62
  mountedCount?: number;
63
63
  }
64
+ /** A component invocation whose entire rendered output is one DOM element. */
65
+ export interface ComponentRootInvocation {
66
+ callsiteId: string;
67
+ componentName: string;
68
+ }
64
69
  export interface ComponentPropContract {
65
70
  name: string;
66
71
  control: "select" | "boolean" | "text";
@@ -93,4 +98,10 @@ export interface ComponentRuntimeAdapter {
93
98
  * fail closed instead of broadening a semantic override.
94
99
  */
95
100
  getCallsiteMultiplicity?(callsiteId: string): number | null | undefined;
101
+ /**
102
+ * Return, nearest first, the invocations whose entire rendered output is
103
+ * this element. A runtime that cannot read the element's ancestry must
104
+ * return null so structural identity fails closed.
105
+ */
106
+ rootInvocations?(element: HTMLElement): ComponentRootInvocation[] | null;
96
107
  }
@@ -0,0 +1,35 @@
1
+ import type { DraftContents } from "../changes/draftChanges.js";
2
+ export type DraftTarget = {
3
+ readonly kind: "application";
4
+ readonly route: string;
5
+ } | {
6
+ readonly kind: "html";
7
+ readonly artifactId: string;
8
+ readonly path: string;
9
+ };
10
+ /** A draft's ID is its target key, so every frame showing the same page or iteration edits one draft. */
11
+ export interface Draft {
12
+ readonly id: string;
13
+ readonly target: DraftTarget;
14
+ readonly revision: number;
15
+ readonly contents: DraftContents;
16
+ readonly sketchIds: readonly string[];
17
+ }
18
+ export interface DraftSnapshot {
19
+ readonly projectId: string;
20
+ readonly drafts: readonly Draft[];
21
+ }
22
+ export declare function applicationTarget(url?: string): {
23
+ kind: "application";
24
+ route: string;
25
+ };
26
+ export declare function htmlTarget(artifactId: string): {
27
+ kind: "html";
28
+ artifactId: string;
29
+ path: string;
30
+ };
31
+ /** Maps a captured document URL to the draft that owns it: an iteration preview or an application page. */
32
+ export declare function documentTarget(url: string): DraftTarget;
33
+ export declare function targetKey(target: DraftTarget): string;
34
+ export declare function targetFromKey(key: string): DraftTarget | null;
35
+ export declare function isDraftTarget(value: unknown): value is DraftTarget;
@@ -0,0 +1,45 @@
1
+ import { normalizeUrl, normalizedUrlKey } from "../canvas/normalizeUrl.js";
2
+ const ARTIFACT_PATH = /^\/__nudge_ui__\/artifacts\/([a-f0-9-]{36})\//i;
3
+ export function applicationTarget(url = window.location.href) {
4
+ const page = new URL(url, window.location.href);
5
+ page.searchParams.delete("nudge-ui");
6
+ page.searchParams.sort();
7
+ const normalized = normalizeUrl(page.href);
8
+ return { kind: "application", route: normalizedUrlKey(normalized) };
9
+ }
10
+ export function htmlTarget(artifactId) {
11
+ return { kind: "html", artifactId, path: `.nudge/artifacts/${artifactId}/document.html` };
12
+ }
13
+ /** Maps a captured document URL to the draft that owns it: an iteration preview or an application page. */
14
+ export function documentTarget(url) {
15
+ const artifactId = ARTIFACT_PATH.exec(new URL(url, window.location.href).pathname)?.[1];
16
+ return artifactId ? htmlTarget(artifactId) : applicationTarget(url);
17
+ }
18
+ export function targetKey(target) {
19
+ return target.kind === "application" ? `application:${target.route}` : `html:${target.artifactId}`;
20
+ }
21
+ export function targetFromKey(key) {
22
+ if (key.startsWith("html:"))
23
+ return htmlTarget(key.slice("html:".length));
24
+ if (!key.startsWith("application:"))
25
+ return null;
26
+ const target = { kind: "application", route: key.slice("application:".length) };
27
+ return isDraftTarget(target) ? target : null;
28
+ }
29
+ export function isDraftTarget(value) {
30
+ if (!value || typeof value !== "object")
31
+ return false;
32
+ const target = value;
33
+ if (target.kind === "application") {
34
+ if (typeof target.route !== "string")
35
+ return false;
36
+ try {
37
+ return applicationTarget(target.route).route === target.route;
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ return target.kind === "html" && typeof target.artifactId === "string"
44
+ && target.path === `.nudge/artifacts/${target.artifactId}/document.html`;
45
+ }
@@ -0,0 +1,28 @@
1
+ import { type FrameContent } from "../canvas/frameContent.js";
2
+ import { type DraftContents } from "../changes/draftChanges.js";
3
+ import type { SketchDocument } from "../sketch/model.js";
4
+ import { type Draft, type DraftSnapshot } from "./model.js";
5
+ export declare function draftIdForContent(content: FrameContent): string;
6
+ export declare function draftIdForCard(cardId: string): string | null;
7
+ export declare function sketchOwner(document: SketchDocument): string | null;
8
+ export declare function getDraft(id: string): Draft | null;
9
+ export declare function getDraftForCard(cardId: string): Draft | null;
10
+ export declare function getDraftContentsForCard(cardId: string): DraftContents | null;
11
+ export declare function persistDrafts(): void;
12
+ /** Replaces every in-memory draft with the stored drafts, so a tab that takes over never writes stale edits. */
13
+ export declare function loadDrafts(nextProjectId: string, options?: {
14
+ persistent?: boolean;
15
+ }): void;
16
+ export declare function activateDraftForCard(cardId: string): void;
17
+ export declare function activateDraft(draftId: string, preferredCardId?: string): void;
18
+ export declare function clearSavedIterationDraft(cardId: string, expectedRevision?: number): boolean;
19
+ /** Returns the first preferred frame that exists, then the first frame. */
20
+ export declare function getEditableCardId(...preferredCardIds: readonly (string | null | undefined)[]): string | null;
21
+ export declare function assignSketchToDraft(draftId: string, sketchId: string): void;
22
+ export declare function sketchBelongsToCard(document: SketchDocument, cardId: string | null): boolean;
23
+ export declare function subscribeDrafts(listener: () => void): () => void;
24
+ export declare function getDraftsSnapshot(): DraftSnapshot;
25
+ export declare function useDrafts(): DraftSnapshot;
26
+ /** Discards stored and in-memory drafts for the current project. */
27
+ export declare function clearDrafts(): void;
28
+ export declare function resetDrafts(): void;
@@ -0,0 +1,231 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { getCanvasCards, selectCard, focusCanvasCards, activateIframeWorkspace } from "../canvas/canvasStore.js";
3
+ import { contentEditTarget } from "../canvas/frameContent.js";
4
+ import { deserializeChange, isSerializableChange, serializeChange, } from "../changes/codecs.js";
5
+ import { getActiveDraftChanges, getDraftChanges, restoreDraftChanges, subscribeDraftChanges, setActiveDraft, clearDraftChanges, listDraftIds, resetDraftChanges, } from "../changes/draftChanges.js";
6
+ import { refreshDraftPreview } from "../changes/changesLog.js";
7
+ import { isStructuralChange } from "../projection/structuralProjection.js";
8
+ import { canWriteWorkspace } from "../canvas/workspaceLease.js";
9
+ import { getSketchStoreSnapshot, subscribeSketchStore } from "../sketch/store.js";
10
+ import { documentTarget, isDraftTarget, targetFromKey, targetKey } from "./model.js";
11
+ let projectId = "";
12
+ let persistent = true;
13
+ /** Overrides the capture-URL owner for sketches drawn while another draft was active. */
14
+ const sketchOwners = new Map();
15
+ const contentDrafts = new WeakMap();
16
+ const listeners = new Set();
17
+ let snapshot = null;
18
+ function publish() {
19
+ snapshot = null;
20
+ for (const listener of listeners)
21
+ listener();
22
+ }
23
+ subscribeDraftChanges(publish);
24
+ subscribeSketchStore(publish);
25
+ function storageKey() { return `nudge-ui-drafts:${projectId}:v2`; }
26
+ export function draftIdForContent(content) {
27
+ let id = contentDrafts.get(content);
28
+ if (id === undefined) {
29
+ id = targetKey(contentEditTarget(content));
30
+ contentDrafts.set(content, id);
31
+ }
32
+ return id;
33
+ }
34
+ export function draftIdForCard(cardId) {
35
+ const card = getCanvasCards().find((candidate) => candidate.id === cardId);
36
+ return card ? draftIdForContent(card.content) : null;
37
+ }
38
+ export function sketchOwner(document) {
39
+ const owner = sketchOwners.get(document.id);
40
+ if (owner)
41
+ return owner;
42
+ try {
43
+ return targetKey(documentTarget(document.capture.url));
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ function readDraft(id, target) {
50
+ const workspace = getDraftChanges(id);
51
+ return {
52
+ id,
53
+ target,
54
+ revision: workspace.revision,
55
+ contents: { changes: workspace.changes, structuralChanges: workspace.structuralChanges },
56
+ sketchIds: getSketchStoreSnapshot().documents.filter((document) => sketchOwner(document) === id).map((document) => document.id),
57
+ };
58
+ }
59
+ export function getDraft(id) {
60
+ const target = targetFromKey(id);
61
+ return target ? readDraft(id, target) : null;
62
+ }
63
+ export function getDraftForCard(cardId) {
64
+ const id = draftIdForCard(cardId);
65
+ return id ? getDraft(id) : null;
66
+ }
67
+ export function getDraftContentsForCard(cardId) {
68
+ const draft = getDraftForCard(cardId);
69
+ return draft ? { changes: draft.contents.changes, structuralChanges: draft.contents.structuralChanges } : null;
70
+ }
71
+ export function persistDrafts() {
72
+ if (!projectId || !persistent || !canWriteWorkspace())
73
+ return;
74
+ const sketches = getSketchStoreSnapshot();
75
+ const liveSketches = new Set(sketches.documents.map((document) => document.id));
76
+ const value = {
77
+ schemaVersion: 2, projectId,
78
+ drafts: listDraftIds().flatMap((id) => {
79
+ const target = targetFromKey(id);
80
+ const workspace = getDraftChanges(id);
81
+ if (!target || workspace.changes.length + workspace.structuralChanges.length === 0)
82
+ return [];
83
+ return [{
84
+ target,
85
+ revision: workspace.revision,
86
+ contents: {
87
+ changes: workspace.changes.flatMap((change) => {
88
+ const serialized = serializeChange(change);
89
+ return serialized ? [serialized] : [];
90
+ }),
91
+ structuralChanges: workspace.structuralChanges,
92
+ },
93
+ }];
94
+ }),
95
+ sketchOwners: Object.fromEntries([...sketchOwners].filter(([id]) => !sketches.ready || liveSketches.has(id))),
96
+ };
97
+ try {
98
+ localStorage.setItem(storageKey(), JSON.stringify(value));
99
+ }
100
+ catch {
101
+ return;
102
+ }
103
+ }
104
+ function validPersisted(value) {
105
+ if (!value || typeof value !== "object")
106
+ return false;
107
+ const candidate = value;
108
+ if (candidate.schemaVersion !== 2 || candidate.projectId !== projectId || !Array.isArray(candidate.drafts))
109
+ return false;
110
+ const targets = new Set();
111
+ for (const draft of candidate.drafts) {
112
+ if (!draft || !isDraftTarget(draft.target) || targets.has(targetKey(draft.target))
113
+ || !Number.isSafeInteger(draft.revision) || draft.revision < 1
114
+ || !draft.contents || !Array.isArray(draft.contents.changes) || !draft.contents.changes.every(isSerializableChange)
115
+ || !Array.isArray(draft.contents.structuralChanges) || !draft.contents.structuralChanges.every(isStructuralChange))
116
+ return false;
117
+ const textIds = draft.contents.changes.filter((change) => change.kind === "text-content").map((change) => "id" in change ? change.id : "");
118
+ if (new Set(textIds).size !== textIds.length)
119
+ return false;
120
+ targets.add(targetKey(draft.target));
121
+ }
122
+ return !!candidate.sketchOwners && typeof candidate.sketchOwners === "object" && !Array.isArray(candidate.sketchOwners)
123
+ && Object.values(candidate.sketchOwners).every((id) => typeof id === "string" && targetFromKey(id) !== null);
124
+ }
125
+ /** Replaces every in-memory draft with the stored drafts, so a tab that takes over never writes stale edits. */
126
+ export function loadDrafts(nextProjectId, options = {}) {
127
+ projectId = nextProjectId;
128
+ persistent = options.persistent ?? true;
129
+ sketchOwners.clear();
130
+ resetDraftChanges();
131
+ try {
132
+ const raw = persistent ? localStorage.getItem(storageKey()) : null;
133
+ const stored = raw ? JSON.parse(raw) : null;
134
+ if (validPersisted(stored)) {
135
+ for (const draft of stored.drafts) {
136
+ restoreDraftChanges(targetKey(draft.target), { changes: draft.contents.changes.map(deserializeChange), structuralChanges: draft.contents.structuralChanges }, draft.revision);
137
+ }
138
+ for (const [sketchId, draftId] of Object.entries(stored.sketchOwners))
139
+ sketchOwners.set(sketchId, draftId);
140
+ }
141
+ }
142
+ catch {
143
+ resetDraftChanges();
144
+ sketchOwners.clear();
145
+ }
146
+ publish();
147
+ refreshDraftPreview();
148
+ }
149
+ export function activateDraftForCard(cardId) {
150
+ const draftId = draftIdForCard(cardId);
151
+ if (draftId)
152
+ activateDraft(draftId, cardId);
153
+ }
154
+ export function activateDraft(draftId, preferredCardId) {
155
+ const previous = getActiveDraftChanges().draftId;
156
+ setActiveDraft(draftId, () => revealDraft(draftId, preferredCardId));
157
+ if (previous !== draftId)
158
+ refreshDraftPreview();
159
+ }
160
+ function revealDraft(draftId, preferredCardId) {
161
+ const cards = getCanvasCards();
162
+ let frame = cards.find((card) => card.id === preferredCardId && draftIdForContent(card.content) === draftId)
163
+ ?? cards.find((card) => draftIdForContent(card.content) === draftId);
164
+ const target = targetFromKey(draftId);
165
+ if (!frame && target?.kind === "application") {
166
+ const original = cards.find((card) => card.id === preferredCardId && card.content.kind === "route");
167
+ if (original) {
168
+ selectCard(original.id);
169
+ frame = activateIframeWorkspace(target.route, { width: window.innerWidth, height: window.innerHeight }, { replaceActiveCard: true }) ?? undefined;
170
+ }
171
+ }
172
+ if (!frame)
173
+ return;
174
+ activateDraftForCard(frame.id);
175
+ selectCard(frame.id);
176
+ focusCanvasCards([frame.id]);
177
+ }
178
+ export function clearSavedIterationDraft(cardId, expectedRevision) {
179
+ const draftId = draftIdForCard(cardId);
180
+ return !!draftId && clearDraftChanges(draftId, expectedRevision);
181
+ }
182
+ /** Returns the first preferred frame that exists, then the first frame. */
183
+ export function getEditableCardId(...preferredCardIds) {
184
+ const cards = getCanvasCards();
185
+ return preferredCardIds.find((id) => !!id && cards.some((card) => card.id === id)) ?? cards[0]?.id ?? null;
186
+ }
187
+ export function assignSketchToDraft(draftId, sketchId) {
188
+ if (sketchOwners.get(sketchId) === draftId || !targetFromKey(draftId))
189
+ return;
190
+ sketchOwners.set(sketchId, draftId);
191
+ publish();
192
+ }
193
+ export function sketchBelongsToCard(document, cardId) {
194
+ if (!projectId || !cardId)
195
+ return true;
196
+ const draftId = draftIdForCard(cardId);
197
+ return !!draftId && sketchOwner(document) === draftId;
198
+ }
199
+ export function subscribeDrafts(listener) {
200
+ listeners.add(listener);
201
+ return () => listeners.delete(listener);
202
+ }
203
+ export function getDraftsSnapshot() {
204
+ if (!snapshot) {
205
+ const ids = new Set([...listDraftIds(), ...getSketchStoreSnapshot().documents.flatMap((document) => sketchOwner(document) ?? [])]);
206
+ snapshot = { projectId, drafts: [...ids].flatMap((id) => getDraft(id) ?? []) };
207
+ }
208
+ return snapshot;
209
+ }
210
+ export function useDrafts() { return useSyncExternalStore(subscribeDrafts, getDraftsSnapshot, getDraftsSnapshot); }
211
+ /** Discards stored and in-memory drafts for the current project. */
212
+ export function clearDrafts() {
213
+ if (projectId && persistent && canWriteWorkspace()) {
214
+ try {
215
+ localStorage.removeItem(storageKey());
216
+ }
217
+ catch {
218
+ return;
219
+ }
220
+ }
221
+ sketchOwners.clear();
222
+ resetDraftChanges();
223
+ publish();
224
+ }
225
+ export function resetDrafts() {
226
+ projectId = "";
227
+ persistent = true;
228
+ sketchOwners.clear();
229
+ resetDraftChanges();
230
+ publish();
231
+ }