nudge-ui 0.2.1 → 0.2.3

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 (258) hide show
  1. package/README.md +164 -46
  2. package/dist/client.mjs +252 -54
  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 +41 -32
  7. package/dist/hosts/astro/integration.d.ts +2 -2
  8. package/dist/hosts/astro/integration.js +9 -4
  9. package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
  10. package/dist/hosts/astro/markdownIdentity.js +81 -0
  11. package/dist/hosts/environment.d.ts +15 -0
  12. package/dist/hosts/environment.js +16 -0
  13. package/dist/hosts/next/manifest.js +1 -1
  14. package/dist/hosts/next/sidecar.js +12 -2
  15. package/dist/hosts/next/wrapper.d.ts +2 -0
  16. package/dist/hosts/next/wrapper.js +2 -1
  17. package/dist/hosts/projectBridge.d.ts +1 -0
  18. package/dist/hosts/projectBridge.js +1 -0
  19. package/dist/hosts/static/manifest.js +3 -1
  20. package/dist/hosts/static/server.js +4 -0
  21. package/dist/hosts/vite/vite.d.ts +4 -0
  22. package/dist/hosts/vite/vite.js +28 -5
  23. package/dist/inspector/agent/McpConnectionDialog.d.ts +3 -2
  24. package/dist/inspector/agent/McpConnectionDialog.js +23 -22
  25. package/dist/inspector/agent/activity.d.ts +18 -0
  26. package/dist/inspector/agent/activity.js +70 -0
  27. package/dist/inspector/agent/client.d.ts +8 -0
  28. package/dist/inspector/agent/client.js +65 -5
  29. package/dist/inspector/agent/connectionStatus.d.ts +3 -3
  30. package/dist/inspector/agent/connectionStatus.js +9 -2
  31. package/dist/inspector/agent/httpTransport.d.ts +6 -0
  32. package/dist/inspector/agent/httpTransport.js +50 -3
  33. package/dist/inspector/agent/protocol.d.ts +9 -3
  34. package/dist/inspector/agent/protocol.js +1 -1
  35. package/dist/inspector/agent/verification.d.ts +11 -1
  36. package/dist/inspector/agent/verification.js +53 -25
  37. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
  38. package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
  39. package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
  40. package/dist/inspector/canvas/CanvasCard.js +133 -38
  41. package/dist/inspector/canvas/CanvasElementOverlay.js +200 -22
  42. package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
  43. package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
  44. package/dist/inspector/canvas/CanvasToolbar.js +8 -4
  45. package/dist/inspector/canvas/CanvasWorkspace.js +243 -56
  46. package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
  47. package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
  48. package/dist/inspector/canvas/agentPresentation.js +64 -52
  49. package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
  50. package/dist/inspector/canvas/canvasGestures.js +1 -1
  51. package/dist/inspector/canvas/canvasStore.d.ts +46 -82
  52. package/dist/inspector/canvas/canvasStore.js +251 -284
  53. package/dist/inspector/canvas/frameContent.d.ts +21 -0
  54. package/dist/inspector/canvas/frameContent.js +44 -0
  55. package/dist/inspector/canvas/frameProtocol.d.ts +34 -4
  56. package/dist/inspector/canvas/frameProtocol.js +9 -2
  57. package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -1
  58. package/dist/inspector/canvas/keyboardShortcuts.js +5 -3
  59. package/dist/inspector/canvas/layout.d.ts +52 -0
  60. package/dist/inspector/canvas/layout.js +106 -0
  61. package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
  62. package/dist/inspector/canvas/linkEligibility.js +0 -29
  63. package/dist/inspector/canvas/projection.d.ts +15 -5
  64. package/dist/inspector/canvas/projection.js +69 -13
  65. package/dist/inspector/canvas/rendererBootstrap.js +45 -4
  66. package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
  67. package/dist/inspector/canvas/rendererElementSelector.js +102 -45
  68. package/dist/inspector/canvas/rendererStylesheet.js +2 -2
  69. package/dist/inspector/canvas/resizeHandleScale.js +1 -1
  70. package/dist/inspector/canvas/sessionStore.d.ts +11 -26
  71. package/dist/inspector/canvas/sessionStore.js +43 -183
  72. package/dist/inspector/canvas/staleChangeDetector.js +9 -3
  73. package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
  74. package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
  75. package/dist/inspector/canvas/viewStore.d.ts +38 -0
  76. package/dist/inspector/canvas/viewStore.js +104 -0
  77. package/dist/inspector/canvas/wheelPan.d.ts +8 -0
  78. package/dist/inspector/canvas/wheelPan.js +8 -0
  79. package/dist/inspector/changes/changesLog.d.ts +6 -5
  80. package/dist/inspector/changes/changesLog.js +40 -32
  81. package/dist/inspector/changes/draftChanges.d.ts +56 -0
  82. package/dist/inspector/changes/draftChanges.js +300 -0
  83. package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
  84. package/dist/inspector/changes/previewDiagnostics.js +11 -25
  85. package/dist/inspector/clientManifest.d.ts +3 -1
  86. package/dist/inspector/clientManifest.js +1 -0
  87. package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
  88. package/dist/inspector/comments/CommentsOverlay.js +250 -0
  89. package/dist/inspector/comments/element.d.ts +6 -0
  90. package/dist/inspector/comments/element.js +47 -0
  91. package/dist/inspector/comments/store.d.ts +29 -0
  92. package/dist/inspector/comments/store.js +121 -0
  93. package/dist/inspector/comments/verification.d.ts +5 -0
  94. package/dist/inspector/comments/verification.js +52 -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 +26 -35
  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 +173 -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.d.ts +10 -0
  111. package/dist/inspector/overlay/DomNavigator.js +46 -0
  112. package/dist/inspector/overlay/InspectorOverlay.js +6 -2
  113. package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
  114. package/dist/inspector/overlay/clickPolicy.js +34 -3
  115. package/dist/inspector/overlay/elementSelector.d.ts +1 -1
  116. package/dist/inspector/overlay/elementSelector.js +18 -7
  117. package/dist/inspector/overlay/interactionStyles.js +3 -1
  118. package/dist/inspector/overlay/spacingGestures.d.ts +15 -3
  119. package/dist/inspector/overlay/spacingGestures.js +88 -13
  120. package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
  121. package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
  122. package/dist/inspector/projection/renderedInstance.d.ts +2 -1
  123. package/dist/inspector/projection/renderedInstance.js +9 -5
  124. package/dist/inspector/projection/structuralProjection.d.ts +2 -1
  125. package/dist/inspector/projection/structuralProjection.js +19 -15
  126. package/dist/inspector/projection/textProjection.d.ts +2 -1
  127. package/dist/inspector/projection/textProjection.js +8 -4
  128. package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
  129. package/dist/inspector/prompt/clipboardHandoff.d.ts +10 -18
  130. package/dist/inspector/prompt/clipboardHandoff.js +92 -143
  131. package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
  132. package/dist/inspector/prompt/generatePrompt.js +17 -16
  133. package/dist/inspector/runtime/runtimeConfig.d.ts +10 -1
  134. package/dist/inspector/runtime/runtimeConfig.js +24 -0
  135. package/dist/inspector/selection/hierarchy.d.ts +6 -0
  136. package/dist/inspector/selection/hierarchy.js +8 -0
  137. package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
  138. package/dist/inspector/settings/SettingsDialog.js +22 -9
  139. package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
  140. package/dist/inspector/shell/AgentStatusButton.js +36 -0
  141. package/dist/inspector/shell/AppShell.js +4 -2
  142. package/dist/inspector/shell/ChangesLog.js +47 -27
  143. package/dist/inspector/shell/CopyPromptButton.js +146 -90
  144. package/dist/inspector/shell/EmptyState.js +9 -8
  145. package/dist/inspector/shell/InspectorShell.js +14 -25
  146. package/dist/inspector/shell/PeekOriginalButton.js +24 -23
  147. package/dist/inspector/shell/originalPreview.js +5 -5
  148. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  149. package/dist/inspector/shell/shortcuts.js +11 -0
  150. package/dist/inspector/sketch/SketchEditor.js +14 -14
  151. package/dist/inspector/sketch/SketchEntryButton.js +1 -1
  152. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
  153. package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
  154. package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
  155. package/dist/inspector/sketch/SketchOverlay.js +7 -7
  156. package/dist/inspector/sketch/SketchWorkspace.js +18 -12
  157. package/dist/inspector/sketch/interaction.d.ts +1 -0
  158. package/dist/inspector/sketch/interaction.js +8 -4
  159. package/dist/inspector/sketch/model.d.ts +5 -0
  160. package/dist/inspector/sketch/model.js +6 -2
  161. package/dist/inspector/sketch/raster.d.ts +1 -2
  162. package/dist/inspector/sketch/raster.js +68 -4
  163. package/dist/inspector/sketch/store.js +48 -23
  164. package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
  165. package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
  166. package/dist/inspector/styleEditors/BorderEditor.js +49 -78
  167. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
  168. package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
  169. package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
  170. package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
  171. package/dist/inspector/styleEditors/ColorPicker.js +10 -4
  172. package/dist/inspector/styleEditors/GapField.js +3 -5
  173. package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
  174. package/dist/inspector/styleEditors/GridChildSection.js +18 -9
  175. package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
  176. package/dist/inspector/styleEditors/GridPicker.js +8 -2
  177. package/dist/inspector/styleEditors/GridSection.js +1 -1
  178. package/dist/inspector/styleEditors/GridValueField.js +19 -14
  179. package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
  180. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
  181. package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
  182. package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
  183. package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
  184. package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
  185. package/dist/inspector/styleEditors/LayoutSection.js +32 -11
  186. package/dist/inspector/styleEditors/SpacingBox.js +16 -9
  187. package/dist/inspector/styleEditors/Typography.js +11 -3
  188. package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
  189. package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
  190. package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
  191. package/dist/inspector/styleEditors/gradientValue.js +114 -0
  192. package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
  193. package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
  194. package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
  195. package/dist/inspector/styleEditors/layoutValue.js +0 -42
  196. package/dist/inspector/styleEditors/nudgeValue.d.ts +4 -4
  197. package/dist/inspector/styleEditors/nudgeValue.js +41 -11
  198. package/dist/inspector/tokens/TokenField.d.ts +7 -0
  199. package/dist/inspector/tokens/TokenField.js +49 -22
  200. package/dist/inspector/tokens/TokenLabel.js +5 -14
  201. package/dist/inspector/tokens/TokensPanel.js +2 -1
  202. package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
  203. package/dist/inspector/tokens/dragPointerLock.js +70 -0
  204. package/dist/inspector/tokens/editActions.d.ts +1 -1
  205. package/dist/inspector/tokens/editActions.js +1 -1
  206. package/dist/inspector/ui/AtRuleContext.js +4 -6
  207. package/dist/inspector/ui/Button.d.ts +6 -2
  208. package/dist/inspector/ui/Button.js +4 -3
  209. package/dist/inspector/ui/ControlSurface.d.ts +2 -2
  210. package/dist/inspector/ui/ControlSurface.js +4 -3
  211. package/dist/inspector/ui/Disclosure.d.ts +22 -0
  212. package/dist/inspector/ui/Disclosure.js +13 -0
  213. package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
  214. package/dist/inspector/ui/InlineStyleWarning.js +13 -0
  215. package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
  216. package/dist/inspector/ui/InspectorPopover.js +4 -5
  217. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  218. package/dist/inspector/ui/PopoverListbox.js +8 -7
  219. package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
  220. package/dist/inspector/ui/SegmentedControl.js +2 -2
  221. package/dist/inspector/ui/Select.d.ts +6 -1
  222. package/dist/inspector/ui/Select.js +7 -11
  223. package/dist/inspector/ui/SideValuesField.js +1 -1
  224. package/dist/inspector/ui/Tooltip.d.ts +26 -0
  225. package/dist/inspector/ui/Tooltip.js +32 -0
  226. package/dist/inspector/ui/classNames.d.ts +2 -0
  227. package/dist/inspector/ui/classNames.js +4 -0
  228. package/dist/inspector/ui/customCursors.d.ts +23 -0
  229. package/dist/inspector/ui/customCursors.js +122 -0
  230. package/dist/inspector/ui/dragCursor.d.ts +6 -0
  231. package/dist/inspector/ui/dragCursor.js +13 -0
  232. package/dist/inspector/ui/portalContainer.d.ts +2 -0
  233. package/dist/inspector/ui/portalContainer.js +6 -0
  234. package/dist/inspector/ui/styles.js +34 -24
  235. package/dist/inspector/workspace/commands.d.ts +9 -0
  236. package/dist/inspector/workspace/commands.js +82 -0
  237. package/dist/inspector/workspace/controller.d.ts +4 -0
  238. package/dist/inspector/workspace/controller.js +66 -0
  239. package/dist/inspector/workspace/handoff.d.ts +42 -0
  240. package/dist/inspector/workspace/handoff.js +115 -0
  241. package/dist/inspector/workspace/timeline.d.ts +30 -0
  242. package/dist/inspector/workspace/timeline.js +108 -0
  243. package/dist/internal/agent-protocol.d.ts +19 -2
  244. package/dist/internal/agent-protocol.js +13 -0
  245. package/dist/nudge-ui.mjs +560 -299
  246. package/dist/project/artifacts.d.ts +13 -0
  247. package/dist/project/artifacts.js +253 -0
  248. package/dist/transport/artifacts.d.ts +8 -0
  249. package/dist/transport/artifacts.js +9 -0
  250. package/dist/transport/editor.d.ts +20 -1
  251. package/dist/transport/editor.js +44 -11
  252. package/dist/transport/routes.d.ts +6 -1
  253. package/dist/transport/routes.js +6 -1
  254. package/package.json +6 -2
  255. package/dist/inspector/changes/workspaceChanges.d.ts +0 -43
  256. package/dist/inspector/changes/workspaceChanges.js +0 -239
  257. package/dist/inspector/shell/DomNavigation.d.ts +0 -8
  258. package/dist/inspector/shell/DomNavigation.js +0 -30
@@ -78,6 +78,10 @@ export declare function interpretColorValue(value: string, ctx: ColorSemanticsCo
78
78
  * unparseable opacities return an explicit `ok: false`.
79
79
  */
80
80
  export declare function applyColorOpacity(value: string, opacity: string): ColorEditResult;
81
+ /** Applies opacity using RGB channels for hex colors so field edits retain percentage precision. */
82
+ export declare function withColorOpacity(color: string, opacity: string): ColorEditResult;
83
+ /** Preserves the previous opacity when the replacement color has no explicit alpha. */
84
+ export declare function replaceColorPreservingOpacity(previous: string, color: string, context: ColorSemanticsContext): ColorEditResult;
81
85
  /**
82
86
  * Replaces only the color token in a separable color expression. Keeping this
83
87
  * separate from a plain token swap preserves authored alpha modifiers, for
@@ -398,6 +398,23 @@ export function applyColorOpacity(value, opacity) {
398
398
  }
399
399
  return { ok: false, reason: "unsupported" };
400
400
  }
401
+ /** Applies opacity using RGB channels for hex colors so field edits retain percentage precision. */
402
+ export function withColorOpacity(color, opacity) {
403
+ const hex = /^#([\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i.exec(color.trim());
404
+ if (hex) {
405
+ const raw = hex[1];
406
+ const channels = rgbTuple(`#${raw.slice(0, raw.length <= 4 ? 3 : 6)}`);
407
+ color = `rgb(${channels.join(" ")} / 100%)`;
408
+ }
409
+ return applyColorOpacity(color, opacity);
410
+ }
411
+ /** Preserves the previous opacity when the replacement color has no explicit alpha. */
412
+ export function replaceColorPreservingOpacity(previous, color, context) {
413
+ const opacity = interpretColorOpacity(previous, context);
414
+ return opacity && !colorValueHasEmbeddedAlpha(color)
415
+ ? withColorOpacity(color, opacity.value)
416
+ : { ok: true, value: color };
417
+ }
401
418
  function tokenReferenceName(entry) {
402
419
  const name = entry.cssName ?? entry.name;
403
420
  return name.startsWith("--") ? name : null;
@@ -14,6 +14,7 @@ export function createAstroRuntimeConfig(knowledge) {
14
14
  capabilities: {
15
15
  canvas: true,
16
16
  componentSemantics: true,
17
+ domNavigation: true,
17
18
  sourceCoordinates: ASTRO_SOURCE_COORDINATES,
18
19
  scopingSelectorPattern: ASTRO_SCOPING_SELECTOR_PATTERN,
19
20
  },
@@ -1,8 +1,10 @@
1
+ import { handleHtmlArtifactRequest } from "../../project/artifacts.js";
1
2
  import {} from "../../inspector/clientManifest.js";
2
3
  import { createAstroRuntimeConfig } from "./astroRuntimeConfig.js";
3
4
  import { createAstroClientAssetHandler } from "./clientAsset.js";
4
5
  import { NUDGE_UI_CLIENT_PATH, NUDGE_UI_EDITOR_PATH, NUDGE_UI_MANIFEST_PATH, createNudgeUiEditorDocument, isNudgeUiEditorDocumentRequest, } from "../../transport/index.js";
5
6
  import { startOptionalProjectBridge } from "../projectBridge.js";
7
+ import { automationManifestFields } from "../environment.js";
6
8
  const serveClient = createAstroClientAssetHandler();
7
9
  /** Serves the prebuilt client and its host-neutral manifest in Astro dev. */
8
10
  export function createAstroClientTransportPlugin() {
@@ -16,38 +18,9 @@ export function createAstroClientTransportPlugin() {
16
18
  apply: "serve",
17
19
  configureServer(server) {
18
20
  server.middlewares.use((request, response, next) => {
19
- const pathname = new URL(request.url ?? "/", "http://nudge-ui.local").pathname;
20
- if (isNudgeUiEditorDocumentRequest(request.url ?? "/", request.method, request.headers)) {
21
- if (request.method !== "GET" && request.method !== "HEAD") {
22
- response.statusCode = 405;
23
- response.setHeader("Allow", "GET, HEAD");
24
- response.end();
25
- return;
26
- }
27
- response.statusCode = 200;
28
- response.setHeader("Content-Type", "text/html; charset=utf-8");
29
- response.setHeader("Cache-Control", "no-store");
30
- response.end(request.method === "HEAD" ? undefined : createNudgeUiEditorDocument());
31
- return;
32
- }
33
- if (pathname === NUDGE_UI_CLIENT_PATH) {
34
- void serveClient(request, response).catch(next);
35
- return;
36
- }
37
- if (pathname === NUDGE_UI_MANIFEST_PATH) {
38
- void createManifest(server, request, (bridge) => {
39
- projectBridge = bridge;
40
- })
41
- .then((manifest) => {
42
- response.statusCode = 200;
43
- response.setHeader("Content-Type", "application/json; charset=utf-8");
44
- response.setHeader("Cache-Control", "no-store");
45
- response.end(JSON.stringify(manifest));
46
- })
47
- .catch(next);
48
- return;
49
- }
50
- next();
21
+ void handleHtmlArtifactRequest(request, response, server.config.root)
22
+ .then((handled) => { if (!handled)
23
+ handleClientRequest(server, request, response, next); }).catch(next);
51
24
  });
52
25
  },
53
26
  async closeBundle() {
@@ -55,6 +28,40 @@ export function createAstroClientTransportPlugin() {
55
28
  projectBridge = null;
56
29
  },
57
30
  };
31
+ function handleClientRequest(server, request, response, next) {
32
+ const pathname = new URL(request.url ?? "/", "http://nudge-ui.local").pathname;
33
+ if (isNudgeUiEditorDocumentRequest(request.url ?? "/", request.method, request.headers)) {
34
+ if (request.method !== "GET" && request.method !== "HEAD") {
35
+ response.statusCode = 405;
36
+ response.setHeader("Allow", "GET, HEAD");
37
+ response.end();
38
+ return;
39
+ }
40
+ response.statusCode = 200;
41
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
42
+ response.setHeader("Cache-Control", "no-store");
43
+ response.end(request.method === "HEAD" ? undefined : createNudgeUiEditorDocument());
44
+ return;
45
+ }
46
+ if (pathname === NUDGE_UI_CLIENT_PATH) {
47
+ void serveClient(request, response).catch(next);
48
+ return;
49
+ }
50
+ if (pathname === NUDGE_UI_MANIFEST_PATH) {
51
+ void createManifest(server, request, (bridge) => {
52
+ projectBridge = bridge;
53
+ })
54
+ .then((manifest) => {
55
+ response.statusCode = 200;
56
+ response.setHeader("Content-Type", "application/json; charset=utf-8");
57
+ response.setHeader("Cache-Control", "no-store");
58
+ response.end(JSON.stringify(manifest));
59
+ })
60
+ .catch(next);
61
+ return;
62
+ }
63
+ next();
64
+ }
58
65
  }
59
66
  async function createManifest(server, request, setBridge) {
60
67
  // SAFETY: these virtual modules are generated by the shared Nudge UI plugin
@@ -77,6 +84,7 @@ async function createManifest(server, request, setBridge) {
77
84
  appRoot: server.config.root,
78
85
  projectId: runtime.projectId,
79
86
  origin,
87
+ info: (message) => server.config.logger.info(message),
80
88
  warn: (message) => server.config.logger.warn(message),
81
89
  })
82
90
  : null;
@@ -86,6 +94,7 @@ async function createManifest(server, request, setBridge) {
86
94
  revision: 0,
87
95
  runtime,
88
96
  ...(bridge ? { agentBridge: bridge.browser } : {}),
97
+ ...automationManifestFields(),
89
98
  };
90
99
  }
91
100
  function resolvedRequestOrigin(server, request) {
@@ -11,8 +11,8 @@ export interface NudgeUiAstroOptions extends NudgeUiOptions {
11
11
  /**
12
12
  * Nudge UI host Adapter for Astro dev servers (ADR-0011).
13
13
  *
14
- * Dev-only by contract (ADR-0002): unless `command === "dev"` and
15
- * `enabled !== false`, the integration registers nothing at all — no Vite
14
+ * Dev-only by contract (ADR-0002): unless `command === "dev"`,
15
+ * `enabled !== false`, and `NUDGE_UI` is not `0`, the integration registers nothing at all — no Vite
16
16
  * plugins, no injected scripts, no middleware — so `astro build` output is
17
17
  * byte-identical to a project without the integration.
18
18
  *
@@ -1,7 +1,10 @@
1
+ import { fileURLToPath } from "node:url";
1
2
  import { nudgeUi } from "../vite/index.js";
2
3
  import { NUDGE_UI_CLIENT_PATH, NUDGE_UI_MANIFEST_PATH } from "../../transport/index.js";
3
4
  import { createAstroClientTransportPlugin, } from "./clientTransport.js";
4
5
  import { createProjectContextPlugin } from "./projectContext.js";
6
+ import { isNudgeUiEnabled } from "../environment.js";
7
+ import { installMarkdownIdentity } from "./markdownIdentity.js";
5
8
  /**
6
9
  * Adds the prebuilt client as an external module. The inspector dependency
7
10
  * graph therefore never enters Astro's Vite optimizer.
@@ -23,8 +26,8 @@ const MIDDLEWARE_ENTRYPOINT = new URL(import.meta.url.endsWith(".ts") ? "./middl
23
26
  /**
24
27
  * Nudge UI host Adapter for Astro dev servers (ADR-0011).
25
28
  *
26
- * Dev-only by contract (ADR-0002): unless `command === "dev"` and
27
- * `enabled !== false`, the integration registers nothing at all — no Vite
29
+ * Dev-only by contract (ADR-0002): unless `command === "dev"`,
30
+ * `enabled !== false`, and `NUDGE_UI` is not `0`, the integration registers nothing at all — no Vite
28
31
  * plugins, no injected scripts, no middleware — so `astro build` output is
29
32
  * byte-identical to a project without the integration.
30
33
  *
@@ -37,19 +40,21 @@ const MIDDLEWARE_ENTRYPOINT = new URL(import.meta.url.endsWith(".ts") ? "./middl
37
40
  * identity Module.
38
41
  */
39
42
  export function nudgeUiAstro(options = {}) {
40
- const enabled = options.enabled ?? true;
43
+ const enabled = isNudgeUiEnabled(options.enabled);
41
44
  const sharedOptions = { ...options };
42
45
  return {
43
46
  name: "nudge-ui",
44
47
  hooks: {
45
- "astro:config:setup"({ command, updateConfig, injectScript, addMiddleware }) {
48
+ "astro:config:setup"({ command, config, updateConfig, injectScript, addMiddleware }) {
46
49
  if (!enabled || command !== "dev")
47
50
  return;
51
+ const markdown = installMarkdownIdentity(config.markdown, fileURLToPath(config.root));
48
52
  // The shared plugin objects are plain Vite plugin records, but this
49
53
  // monorepo typechecks them against a different Vite major than the
50
54
  // installed Astro resolves internally; the runtime contract
51
55
  // (config/resolveId/load/transform hooks) is identical.
52
56
  updateConfig({
57
+ ...(markdown ? { markdown } : {}),
53
58
  vite: {
54
59
  // SAFETY: Astro and this package resolve different Vite type
55
60
  // versions, but both consume the same runtime Plugin contract.
@@ -0,0 +1,28 @@
1
+ import type { Element, Root } from "hast";
2
+ /** Adds authored Markdown positions through the remark/rehype pipeline. */
3
+ export declare function createMarkdownIdentityPlugin(projectRoot: string): () => (tree: Root, file: {
4
+ path?: string;
5
+ }) => void;
6
+ /** Adds the same identity through Astro's Sätteri visitor API. */
7
+ export declare function createSatteriIdentityPlugin(projectRoot: string): {
8
+ name: string;
9
+ options: {
10
+ position: boolean;
11
+ };
12
+ element: {
13
+ filter: string[];
14
+ visit(node: Readonly<Element>, context: {
15
+ fileURL: URL | undefined;
16
+ setProperty(node: Readonly<Element>, key: string, value: string): void;
17
+ }): void;
18
+ };
19
+ };
20
+ /** Extends supported processors without replacing the consumer's pipeline. */
21
+ export declare function installMarkdownIdentity(markdown: {
22
+ processor?: {
23
+ name: string;
24
+ options: object;
25
+ };
26
+ }, projectRoot: string): {
27
+ rehypePlugins: ReturnType<typeof createMarkdownIdentityPlugin>[];
28
+ } | undefined;
@@ -0,0 +1,81 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ const PROSE_TAGS = [
4
+ "p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li",
5
+ "blockquote", "a", "em", "strong", "del", "code", "pre", "hr", "br",
6
+ "table", "thead", "tbody", "tr", "th", "td",
7
+ ];
8
+ const proseTags = new Set(PROSE_TAGS);
9
+ function attributesFor(node, file, projectRoot) {
10
+ const start = node.position?.start;
11
+ if (!start || start.line < 1 || start.column < 1 || !proseTags.has(node.tagName))
12
+ return {};
13
+ const relative = path.relative(projectRoot, file).replaceAll("\\", "/");
14
+ const attributes = {};
15
+ const properties = node.properties;
16
+ if (properties["data-cid"] === undefined && properties.dataCid === undefined) {
17
+ attributes["data-cid"] = `astro:${node.tagName.charAt(0).toUpperCase()}${node.tagName.slice(1)}`;
18
+ }
19
+ if (properties["data-src"] === undefined && properties.dataSrc === undefined) {
20
+ attributes["data-src"] = `${relative}:${start.line}:${start.column}`;
21
+ }
22
+ return attributes;
23
+ }
24
+ /** Adds authored Markdown positions through the remark/rehype pipeline. */
25
+ export function createMarkdownIdentityPlugin(projectRoot) {
26
+ return function nudgeMarkdownIdentity() {
27
+ return (tree, file) => {
28
+ if (!file.path || !/\.mdx?$/.test(file.path))
29
+ return;
30
+ const sourceFile = file.path;
31
+ const visit = (node) => {
32
+ if (node.type === "element") {
33
+ Object.assign(node.properties, attributesFor(node, sourceFile, projectRoot));
34
+ }
35
+ if ("children" in node) {
36
+ for (const child of node.children)
37
+ visit(child);
38
+ }
39
+ };
40
+ visit(tree);
41
+ };
42
+ };
43
+ }
44
+ /** Adds the same identity through Astro's Sätteri visitor API. */
45
+ export function createSatteriIdentityPlugin(projectRoot) {
46
+ return {
47
+ name: "nudge-ui-markdown-identity",
48
+ options: { position: true },
49
+ element: {
50
+ filter: PROSE_TAGS,
51
+ visit(node, context) {
52
+ if (!context.fileURL || context.fileURL.protocol !== "file:")
53
+ return;
54
+ const file = fileURLToPath(context.fileURL);
55
+ if (!/\.mdx?$/.test(file))
56
+ return;
57
+ for (const [key, value] of Object.entries(attributesFor(node, file, projectRoot))) {
58
+ context.setProperty(node, key, value);
59
+ }
60
+ },
61
+ },
62
+ };
63
+ }
64
+ /** Extends supported processors without replacing the consumer's pipeline. */
65
+ export function installMarkdownIdentity(markdown, projectRoot) {
66
+ const processor = markdown.processor;
67
+ if (!processor)
68
+ return { rehypePlugins: [createMarkdownIdentityPlugin(projectRoot)] };
69
+ if (processor.name !== "unified" && processor.name !== "satteri")
70
+ return undefined;
71
+ // SAFETY: Astro's supported processors expose mutable plugin arrays. This
72
+ // adapter only appends plugins and does not inspect existing entries.
73
+ const options = processor.options;
74
+ if (processor.name === "unified") {
75
+ (options.rehypePlugins ??= []).push(createMarkdownIdentityPlugin(projectRoot));
76
+ }
77
+ else if (processor.name === "satteri") {
78
+ (options.hastPlugins ??= []).push(createSatteriIdentityPlugin(projectRoot));
79
+ }
80
+ return undefined;
81
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The `NUDGE_UI` environment switch shared by every development host.
3
+ *
4
+ * - `0`, `false`, or `off` disables the host Adapter for that run.
5
+ * - `1`, `true`, or `on` also opens the editor in automated browsers, which
6
+ * otherwise receive the plain application.
7
+ */
8
+ export type NudgeUiEnvironmentSwitch = "off" | "on" | null;
9
+ export declare function readNudgeUiEnvironmentSwitch(env?: Readonly<Record<string, string | undefined>>): NudgeUiEnvironmentSwitch;
10
+ /** An explicit `enabled: false` or `NUDGE_UI=0` disables the Adapter. */
11
+ export declare function isNudgeUiEnabled(enabled: boolean | undefined, env?: Readonly<Record<string, string | undefined>>): boolean;
12
+ /** Manifest fields derived from the environment at request time. */
13
+ export declare function automationManifestFields(env?: Readonly<Record<string, string | undefined>>): {
14
+ inspectAutomatedBrowsers?: true;
15
+ };
@@ -0,0 +1,16 @@
1
+ export function readNudgeUiEnvironmentSwitch(env = process.env) {
2
+ const value = env.NUDGE_UI?.trim().toLowerCase();
3
+ if (value === "0" || value === "false" || value === "off")
4
+ return "off";
5
+ if (value === "1" || value === "true" || value === "on")
6
+ return "on";
7
+ return null;
8
+ }
9
+ /** An explicit `enabled: false` or `NUDGE_UI=0` disables the Adapter. */
10
+ export function isNudgeUiEnabled(enabled, env = process.env) {
11
+ return enabled !== false && readNudgeUiEnvironmentSwitch(env) !== "off";
12
+ }
13
+ /** Manifest fields derived from the environment at request time. */
14
+ export function automationManifestFields(env = process.env) {
15
+ return readNudgeUiEnvironmentSwitch(env) === "on" ? { inspectAutomatedBrowsers: true } : {};
16
+ }
@@ -15,7 +15,7 @@ export function buildManifest(input) {
15
15
  stylingSystem: "CSS custom properties",
16
16
  // Canvas shares the Vite host's controller/renderer runtime (ADR-0006);
17
17
  // the mount in every document bootstraps as a renderer inside cards.
18
- capabilities: { canvas: true, componentSemantics: true },
18
+ capabilities: { canvas: true, componentSemantics: true, domNavigation: true },
19
19
  tokenCatalog: [],
20
20
  tokens: [],
21
21
  tokenDiagnostics: [],
@@ -1,3 +1,4 @@
1
+ import { handleHtmlArtifactRequest } from "../../project/artifacts.js";
1
2
  import { createServer } from "node:http";
2
3
  import { createRequire } from "node:module";
3
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
@@ -9,6 +10,7 @@ import { createProjectFileWatcher, } from "../../project/files.js";
9
10
  import { createProjectTokenSnapshot } from "../../project/tokens.js";
10
11
  import { NUDGE_UI_CLIENT_PATH, NUDGE_UI_EDITOR_PATH, NUDGE_UI_MANIFEST_PATH, NUDGE_UI_RELOAD_PATH, NUDGE_UI_ROUTE_PREFIX, createNudgeUiEditorDocument, isNudgeUiEditorDocumentRequest, } from "../../transport/index.js";
11
12
  import { startOptionalProjectBridge } from "../projectBridge.js";
13
+ import { automationManifestFields } from "../environment.js";
12
14
  /**
13
15
  * Loopback-only manifest/reload sidecar (ADR-0010).
14
16
  *
@@ -264,6 +266,7 @@ export async function ensureSidecar(root, options = {}) {
264
266
  ...(options.projectBridgeAllowedOrigins
265
267
  ? { allowedOrigins: options.projectBridgeAllowedOrigins }
266
268
  : {}),
269
+ info: (message) => console.info(message),
267
270
  warn: (message) => console.warn(message),
268
271
  }).then((bridge) => {
269
272
  projectBridge = bridge;
@@ -297,7 +300,10 @@ export async function ensureSidecar(root, options = {}) {
297
300
  contractsTimer = setTimeout(flushContracts, 120);
298
301
  };
299
302
  const server = createServer((req, res) => {
300
- respond(req, res, () => manifest, () => generation, streams, receiveContracts);
303
+ void handleHtmlArtifactRequest(req, res, fsRoot).then((handled) => {
304
+ if (!handled)
305
+ respond(req, res, () => manifest, () => generation, streams, receiveContracts);
306
+ });
301
307
  });
302
308
  const port = await new Promise((resolvePort, rejectPort) => {
303
309
  server.once("error", rejectPort);
@@ -529,7 +535,11 @@ function respond(req, res, currentManifest, currentGeneration, streams, receiveC
529
535
  if (url === NUDGE_UI_MANIFEST_PATH) {
530
536
  // The served snapshot carries its revision so clients can reconcile
531
537
  // against SSE notifications instead of guessing.
532
- const body = `${JSON.stringify({ ...currentManifest(), revision: currentGeneration() })}\n`;
538
+ const body = `${JSON.stringify({
539
+ ...currentManifest(),
540
+ revision: currentGeneration(),
541
+ ...automationManifestFields(),
542
+ })}\n`;
533
543
  res.writeHead(200, {
534
544
  "content-type": "application/json",
535
545
  "cache-control": "no-store",
@@ -28,6 +28,8 @@ export interface NudgeUiNextConfig {
28
28
  }
29
29
  /** Host-specific policy additions passed to the shared source compiler. */
30
30
  export interface NudgeUiNextOptions {
31
+ /** `false` returns the configuration untouched. `NUDGE_UI=0` has the same effect for one run. */
32
+ readonly enabled?: boolean;
31
33
  readonly componentProtocols?: ComponentModuleProtocols;
32
34
  /** Authored workspace directories outside the Next.js application root. */
33
35
  readonly sourceRoots?: readonly string[];
@@ -4,6 +4,7 @@ import { dirname, join, relative, resolve } from "node:path";
4
4
  import { ensureSidecar } from "./sidecar.js";
5
5
  import { buildManifest } from "./manifest.js";
6
6
  import { nudgeUiRepositoryPackagePath } from "./repositoryScope.js";
7
+ import { isNudgeUiEnabled } from "../environment.js";
7
8
  import { NUDGE_UI_EDITOR_QUERY_PARAM, NUDGE_UI_EDITOR_QUERY_VALUE, NUDGE_UI_ROUTE_PREFIX, } from "../../transport/index.js";
8
9
  const NAMESPACE_PREFIX = NUDGE_UI_ROUTE_PREFIX.slice(0, -1);
9
10
  const SUPPORTED_NEXT_RANGE = ">=15.3 <17";
@@ -104,7 +105,7 @@ export function withNudgeUi(config = {}, options = {}) {
104
105
  : () => config;
105
106
  return (phase) => {
106
107
  const resolved = factory(phase);
107
- if (!isDevelopmentEvaluation(phase))
108
+ if (!isDevelopmentEvaluation(phase) || !isNudgeUiEnabled(options.enabled))
108
109
  return resolved;
109
110
  return instrumentConfig(resolved, options);
110
111
  };
@@ -8,6 +8,7 @@ export declare function startOptionalProjectBridge(input: {
8
8
  readonly projectId: string;
9
9
  readonly origin: string;
10
10
  readonly allowedOrigins?: readonly string[];
11
+ readonly info?: (message: string) => void;
11
12
  readonly warn?: (message: string) => void;
12
13
  }): Promise<{
13
14
  browser: ProjectBridgeBrowserConfig;
@@ -53,6 +53,7 @@ async function launchOptionalProjectBridge(input, key) {
53
53
  await runtime.close().catch(() => undefined);
54
54
  throw new Error("the project bridge returned a different project or origin");
55
55
  }
56
+ input.info?.(`[nudge-ui] project bridge ready for ${input.projectId}`);
56
57
  let closed = false;
57
58
  return {
58
59
  browser: { baseUrl: runtime.browser.bridgeUrl, autoConnect: true },
@@ -1,4 +1,5 @@
1
1
  import { NUDGE_UI_MANIFEST_VERSION, NUDGE_UI_RELOAD_PATH } from "../../transport/index.js";
2
+ import { automationManifestFields } from "../environment.js";
2
3
  export { NUDGE_UI_CLIENT_PATH, NUDGE_UI_EDITOR_PATH, NUDGE_UI_MANIFEST_PATH, NUDGE_UI_MOUNT_ID, NUDGE_UI_RELOAD_PATH, NUDGE_UI_ROUTE_PREFIX, } from "../../transport/index.js";
3
4
  /**
4
5
  * Creates the first valid static-HTML runtime snapshot.
@@ -23,7 +24,7 @@ export function createStandaloneRuntimeManifest(projectId, tokenSnapshot = empty
23
24
  // Canvas shares the Vite host's controller/renderer runtime (ADR-0012):
24
25
  // every served document carries the bootstrap, so card iframes boot as
25
26
  // renderers without host-specific code.
26
- capabilities: { canvas: true, componentSemantics: false },
27
+ capabilities: { canvas: true, componentSemantics: false, domNavigation: true },
27
28
  tokenCatalog: tokenSnapshot.tokenCatalog,
28
29
  tokens: tokenSnapshot.tokens,
29
30
  tokenDiagnostics: tokenSnapshot.tokenDiagnostics,
@@ -39,6 +40,7 @@ export function createStandaloneRuntimeManifest(projectId, tokenSnapshot = empty
39
40
  strategy: "reload-document",
40
41
  events: ["ready", "reload"],
41
42
  },
43
+ ...automationManifestFields(),
42
44
  };
43
45
  }
44
46
  function emptyTokenSnapshot() {
@@ -1,3 +1,4 @@
1
+ import { handleHtmlArtifactRequest } from "../../project/artifacts.js";
1
2
  import { createHash } from "node:crypto";
2
3
  import { createServer, } from "node:http";
3
4
  import { constants, existsSync, realpathSync, statSync, } from "node:fs";
@@ -157,6 +158,7 @@ export function createStandaloneServer(options) {
157
158
  appRoot: rootDirectory,
158
159
  projectId,
159
160
  origin: address.url.replace(/\/$/, ""),
161
+ info: (message) => console.info(message),
160
162
  warn: (message) => console.warn(message),
161
163
  }).then((bridge) => {
162
164
  projectBridge = bridge;
@@ -382,6 +384,8 @@ function isWithin(root, candidate) {
382
384
  }
383
385
  async function handleRequest(input) {
384
386
  const { request, response } = input;
387
+ if (await handleHtmlArtifactRequest(request, response, input.rootDirectory))
388
+ return;
385
389
  if (request.method !== "GET" && request.method !== "HEAD") {
386
390
  sendText(response, 405, "Method Not Allowed", { Allow: "GET, HEAD" });
387
391
  return;
@@ -1,5 +1,6 @@
1
1
  import type { Plugin } from "vite";
2
2
  import type { TailwindV3Config, ThemeContract } from "../../css/dialects/index.js";
3
+ import { type NudgeUiRuntimeConfig } from "../../inspector/clientManifest.js";
3
4
  import type { FrameworkHost, FrameworkSupport, ReactOptions } from "./react.js";
4
5
  /**
5
6
  * How to find the project's vanilla-extract theme contract. Loading a module
@@ -18,6 +19,7 @@ export interface VanillaExtractOptions {
18
19
  source?: string;
19
20
  }
20
21
  export interface NudgeUiOptions extends ReactOptions {
22
+ /** `false` registers nothing. `NUDGE_UI=0` has the same effect for one run. */
21
23
  enabled?: boolean;
22
24
  /** Enables experimental DOM parent/child navigation in the Inspector. */
23
25
  debug?: boolean;
@@ -27,6 +29,8 @@ export interface NudgeUiOptions extends ReactOptions {
27
29
  demoPages?: readonly string[];
28
30
  /** Labels for the seeded demo cards in display order, including the primary card. */
29
31
  demoCardLabels?: readonly string[];
32
+ /** Product features exposed by the inspector runtime. Omitted flags remain enabled. */
33
+ features?: NudgeUiRuntimeConfig["features"];
30
34
  /** Explicit project ID for browser-storage keys (defaults to root directory basename). */
31
35
  projectId?: string;
32
36
  /** Optional static v3 config for fixture/app integrations; dynamic configs are not executed. */
@@ -1,3 +1,4 @@
1
+ import { handleHtmlArtifactRequest } from "../../project/artifacts.js";
1
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
3
  import { createRequire } from "node:module";
3
4
  import { basename, join, relative, resolve, sep, } from "node:path";
@@ -8,6 +9,7 @@ import { catalogSourcePath, createViteStylesheetArtifact, isHostApplicationSourc
8
9
  import { interpretDialects } from "../../css/dialects/index.js";
9
10
  import { detectStylingSystem, } from "../../inspector/clientManifest.js";
10
11
  import { loopbackOrigin, startOptionalProjectBridge, } from "../projectBridge.js";
12
+ import { automationManifestFields, isNudgeUiEnabled } from "../environment.js";
11
13
  function isRecord(value) {
12
14
  return typeof value === "object" && value !== null && !Array.isArray(value);
13
15
  }
@@ -80,6 +82,8 @@ function scanCssFiles(rootDir, files = [], dir = rootDir, ignoredDirectory) {
80
82
  export function transformIndexHtmlHtml(html, command, { debug = false, demoBuild = false, demo = false } = {}) {
81
83
  if (command === "build" && !demoBuild)
82
84
  return null;
85
+ if (html.includes("data-nudge-ui-client"))
86
+ return null;
83
87
  const script = demo || demoBuild ? LEGACY_INSPECTOR_SCRIPT : INSPECTOR_SCRIPT;
84
88
  const inject = `\n${debug ? DEBUG_MOUNT_DIV : MOUNT_DIV}\n${script}\n`;
85
89
  if (html.includes("</body>")) {
@@ -101,7 +105,7 @@ function invalidateHmrModules(server, modules, timestamp) {
101
105
  * here has grown a React assumption.
102
106
  */
103
107
  export function createVitePlugins(options = {}, createFramework = null) {
104
- const enabled = options.enabled ?? true;
108
+ const enabled = isNudgeUiEnabled(options.enabled);
105
109
  let root;
106
110
  let buildOutputDirectory;
107
111
  let command = "serve";
@@ -484,6 +488,7 @@ export function createVitePlugins(options = {}, createFramework = null) {
484
488
  revision: 0,
485
489
  runtime: buildRuntimeSnapshot(),
486
490
  ...(projectBridge ? { agentBridge: projectBridge.browser } : {}),
491
+ ...automationManifestFields(),
487
492
  };
488
493
  }
489
494
  function startProjectBridge(server) {
@@ -506,6 +511,7 @@ export function createVitePlugins(options = {}, createFramework = null) {
506
511
  projectId: options.projectId ?? basename(root),
507
512
  origin,
508
513
  ...(allowedOrigins.length > 0 ? { allowedOrigins } : {}),
514
+ info: (message) => server.config.logger.info(message),
509
515
  warn: (message) => server.config.logger.warn(message),
510
516
  }).then((bridge) => {
511
517
  projectBridge = bridge;
@@ -530,7 +536,8 @@ export function createVitePlugins(options = {}, createFramework = null) {
530
536
  ...(options.demo === true ? { demo: true } : {}),
531
537
  ...(options.demoPages === undefined ? {} : { demoPages: options.demoPages }),
532
538
  ...(options.demoCardLabels === undefined ? {} : { demoCardLabels: options.demoCardLabels }),
533
- capabilities: { canvas: true, componentSemantics: framework !== null },
539
+ ...(options.features === undefined ? {} : { features: options.features }),
540
+ capabilities: { canvas: true, componentSemantics: framework !== null, domNavigation: true },
534
541
  tokenCatalog: snapshot.definitions.map((definition) => ({
535
542
  ...definition,
536
543
  declarations: definition.declarations.map((declaration) => ({ ...declaration })),
@@ -551,11 +558,24 @@ export function createVitePlugins(options = {}, createFramework = null) {
551
558
  if (!enabled || env.command !== "serve")
552
559
  return;
553
560
  // Module resolution is a framework concern; without one this host asks nothing of the resolver.
554
- return framework?.viteConfig({
561
+ const frameworkConfig = framework?.viteConfig({
555
562
  projectRoot: userConfig.root ?? process.cwd(),
556
563
  demo: options.demo === true,
557
564
  existingDedupe: userConfig.resolve?.dedupe ?? [],
558
565
  });
566
+ const ignored = userConfig.server?.watch?.ignored;
567
+ return {
568
+ ...frameworkConfig,
569
+ server: {
570
+ watch: {
571
+ ignored: [
572
+ ...(ignored === undefined ? [] : Array.isArray(ignored) ? ignored : [ignored]),
573
+ "**/.nudge",
574
+ "**/.nudge/**",
575
+ ],
576
+ },
577
+ },
578
+ };
559
579
  },
560
580
  configResolved(config) {
561
581
  root = config.root;
@@ -583,6 +603,8 @@ export function createVitePlugins(options = {}, createFramework = null) {
583
603
  if (runtimeWarning)
584
604
  server.config.logger.warn(runtimeWarning);
585
605
  server.middlewares?.use(async (request, response, next) => {
606
+ if (await handleHtmlArtifactRequest(request, response, root ?? process.cwd(), (html) => server.transformIndexHtml(request.url ?? "/", html)))
607
+ return;
586
608
  const pathname = new URL(request.url ?? "/", "http://nudge-ui.local").pathname;
587
609
  const invalidReservedMethod = (pathname === CLIENT_PATH || pathname === MANIFEST_PATH)
588
610
  ? request.method !== "GET"
@@ -598,7 +620,7 @@ export function createVitePlugins(options = {}, createFramework = null) {
598
620
  response.statusCode = 200;
599
621
  response.setHeader("Content-Type", "text/html; charset=utf-8");
600
622
  response.setHeader("Cache-Control", "no-store");
601
- response.end(request.method === "HEAD" ? undefined : createNudgeUiEditorDocument());
623
+ response.end(request.method === "HEAD" ? undefined : await server.transformIndexHtml(request.url ?? NUDGE_UI_EDITOR_PATH, createNudgeUiEditorDocument()));
602
624
  return;
603
625
  }
604
626
  if (pathname === CLIENT_PATH) {
@@ -715,7 +737,8 @@ export function createVitePlugins(options = {}, createFramework = null) {
715
737
  ' demo: true,',
716
738
  ` demoPages: ${JSON.stringify(options.demoPages ?? [])},`,
717
739
  ` demoCardLabels: ${JSON.stringify(options.demoCardLabels ?? [])},`,
718
- ` capabilities: { canvas: true, componentSemantics: ${String(framework !== null)} },`,
740
+ ` features: ${JSON.stringify(options.features ?? {})},`,
741
+ ` capabilities: { canvas: true, componentSemantics: ${String(framework !== null)}, domNavigation: true },`,
719
742
  ' tokenCatalog,',
720
743
  ' tokens,',
721
744
  ' tokenDiagnostics,',