nudge-ui 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/nudge-ui.mjs +3 -1
- package/dist/cli/agentCommand.d.ts +8 -0
- package/dist/cli/agentCommand.js +59 -0
- package/dist/client.mjs +878 -11
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
- package/dist/hosts/astro/clientTransport.js +63 -11
- package/dist/hosts/astro/integration.d.ts +2 -2
- package/dist/hosts/astro/integration.js +4 -3
- package/dist/hosts/environment.d.ts +15 -0
- package/dist/hosts/environment.js +16 -0
- package/dist/hosts/next/sidecar.d.ts +4 -0
- package/dist/hosts/next/sidecar.js +56 -7
- package/dist/hosts/next/wrapper.d.ts +2 -0
- package/dist/hosts/next/wrapper.js +32 -3
- package/dist/hosts/projectBridge.d.ts +21 -0
- package/dist/hosts/projectBridge.js +98 -0
- package/dist/hosts/static/cli.d.ts +1 -1
- package/dist/hosts/static/cli.js +6 -1
- package/dist/hosts/static/manifest.d.ts +1 -1
- package/dist/hosts/static/manifest.js +3 -1
- package/dist/hosts/static/server.js +45 -3
- package/dist/hosts/vite/vite.d.ts +6 -1
- package/dist/hosts/vite/vite.js +82 -15
- package/dist/inspector/agent/McpConnectionDialog.d.ts +5 -4
- package/dist/inspector/agent/McpConnectionDialog.js +20 -37
- package/dist/inspector/agent/client.d.ts +16 -3
- package/dist/inspector/agent/client.js +114 -11
- package/dist/inspector/agent/connectionStatus.d.ts +3 -3
- package/dist/inspector/agent/connectionStatus.js +11 -4
- package/dist/inspector/agent/httpTransport.d.ts +8 -0
- package/dist/inspector/agent/httpTransport.js +78 -18
- package/dist/inspector/agent/protocol.d.ts +11 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +1 -1
- package/dist/inspector/agent/verification.js +69 -12
- package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
- package/dist/inspector/canvas/CanvasCard.js +181 -91
- package/dist/inspector/canvas/CanvasElementOverlay.js +288 -52
- package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
- package/dist/inspector/canvas/CanvasToolbar.js +24 -0
- package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +410 -72
- package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
- package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
- package/dist/inspector/canvas/canvasResize.d.ts +30 -0
- package/dist/inspector/canvas/canvasResize.js +52 -0
- package/dist/inspector/canvas/canvasStore.d.ts +30 -6
- package/dist/inspector/canvas/canvasStore.js +128 -61
- package/dist/inspector/canvas/frameProtocol.d.ts +82 -5
- package/dist/inspector/canvas/frameProtocol.js +63 -2
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -0
- package/dist/inspector/canvas/keyboardShortcuts.js +16 -0
- package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
- package/dist/inspector/canvas/linkEligibility.js +0 -29
- package/dist/inspector/canvas/projection.d.ts +7 -0
- package/dist/inspector/canvas/projection.js +118 -25
- package/dist/inspector/canvas/rendererBootstrap.js +42 -29
- package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
- package/dist/inspector/canvas/rendererElementSelector.js +385 -58
- package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
- package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
- package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
- package/dist/inspector/canvas/sessionStore.d.ts +1 -0
- package/dist/inspector/canvas/sessionStore.js +12 -4
- package/dist/inspector/canvas/staleChangeDetector.js +16 -14
- package/dist/inspector/changes/changesLog.js +31 -1
- package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
- package/dist/inspector/changes/managedStyleProjection.js +3 -3
- package/dist/inspector/changes/structuralTypes.d.ts +4 -0
- package/dist/inspector/changes/workspaceChanges.d.ts +4 -0
- package/dist/inspector/changes/workspaceChanges.js +29 -3
- package/dist/inspector/clientManifest.d.ts +7 -0
- package/dist/inspector/clientManifest.js +12 -0
- package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
- package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
- package/dist/inspector/index.d.ts +2 -0
- package/dist/inspector/index.js +80 -32
- package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
- package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
- package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
- package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
- package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
- package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
- package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
- package/dist/inspector/inline-text/textEditChange.js +165 -0
- package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
- package/dist/inspector/inspection/selectionProperty.js +1 -0
- package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
- package/dist/inspector/overlay/DomNavigator.js +40 -0
- package/dist/inspector/overlay/InspectorOverlay.js +6 -2
- package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
- package/dist/inspector/overlay/clickPolicy.js +34 -3
- package/dist/inspector/overlay/elementSelector.d.ts +1 -1
- package/dist/inspector/overlay/elementSelector.js +18 -7
- package/dist/inspector/overlay/selectedGeometry.js +21 -2
- package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
- package/dist/inspector/overlay/spacingGestures.js +351 -0
- package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
- package/dist/inspector/overlay/structuralGestures.js +12 -10
- package/dist/inspector/projection/managedStylesheet.js +4 -1
- package/dist/inspector/projection/renderedInstance.d.ts +2 -0
- package/dist/inspector/projection/renderedInstance.js +5 -1
- package/dist/inspector/projection/structuralProjection.d.ts +2 -0
- package/dist/inspector/projection/structuralProjection.js +85 -1
- package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
- package/dist/inspector/projection/textProjection.d.ts +2 -0
- package/dist/inspector/projection/textProjection.js +10 -4
- package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
- package/dist/inspector/prompt/clipboardHandoff.js +3 -3
- package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
- package/dist/inspector/prompt/generatePrompt.js +19 -2
- package/dist/inspector/runtime/editorShell.d.ts +2 -0
- package/dist/inspector/runtime/editorShell.js +4 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +5 -1
- package/dist/inspector/runtime/runtimeConfig.js +4 -0
- package/dist/inspector/selection/hierarchy.d.ts +6 -0
- package/dist/inspector/selection/hierarchy.js +8 -0
- package/dist/inspector/selection/selectionStore.js +2 -0
- package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
- package/dist/inspector/settings/SettingsDialog.js +12 -2
- package/dist/inspector/shell/AppShell.js +5 -1
- package/dist/inspector/shell/ChangesLog.js +26 -22
- package/dist/inspector/shell/CopyPromptButton.js +102 -19
- package/dist/inspector/shell/EmptyState.js +1 -1
- package/dist/inspector/shell/InspectorShell.d.ts +0 -1
- package/dist/inspector/shell/InspectorShell.js +38 -42
- package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
- package/dist/inspector/shell/PeekOriginalButton.js +57 -0
- package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
- package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
- package/dist/inspector/shell/originalPreview.d.ts +9 -0
- package/dist/inspector/shell/originalPreview.js +61 -0
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +15 -0
- package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
- package/dist/inspector/sketch/SketchChanges.js +72 -0
- package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
- package/dist/inspector/sketch/SketchEditor.js +402 -0
- package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
- package/dist/inspector/sketch/SketchEntryButton.js +11 -0
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
- package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
- package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
- package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
- package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
- package/dist/inspector/sketch/SketchOverlay.js +420 -0
- package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
- package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
- package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
- package/dist/inspector/sketch/SketchWorkspace.js +152 -0
- package/dist/inspector/sketch/capture.d.ts +55 -0
- package/dist/inspector/sketch/capture.js +350 -0
- package/dist/inspector/sketch/freehand.d.ts +13 -0
- package/dist/inspector/sketch/freehand.js +64 -0
- package/dist/inspector/sketch/geometry.d.ts +17 -0
- package/dist/inspector/sketch/geometry.js +98 -0
- package/dist/inspector/sketch/handoff.d.ts +21 -0
- package/dist/inspector/sketch/handoff.js +72 -0
- package/dist/inspector/sketch/interaction.d.ts +21 -0
- package/dist/inspector/sketch/interaction.js +98 -0
- package/dist/inspector/sketch/model.d.ts +104 -0
- package/dist/inspector/sketch/model.js +282 -0
- package/dist/inspector/sketch/persistence.d.ts +21 -0
- package/dist/inspector/sketch/persistence.js +205 -0
- package/dist/inspector/sketch/raster.d.ts +22 -0
- package/dist/inspector/sketch/raster.js +203 -0
- package/dist/inspector/sketch/sketchNote.d.ts +10 -0
- package/dist/inspector/sketch/sketchNote.js +30 -0
- package/dist/inspector/sketch/store.d.ts +47 -0
- package/dist/inspector/sketch/store.js +434 -0
- package/dist/inspector/styleEditors/BorderEditor.js +15 -19
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
- package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
- package/dist/inspector/styleEditors/ColorPicker.js +11 -36
- package/dist/inspector/styleEditors/GapField.d.ts +3 -2
- package/dist/inspector/styleEditors/GapField.js +2 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/LayoutSection.js +11 -8
- package/dist/inspector/styleEditors/SpacingBox.js +31 -33
- package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
- package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
- package/dist/inspector/styleEditors/layoutValue.js +3 -0
- package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
- package/dist/inspector/styleEditors/nudgeValue.js +93 -5
- package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
- package/dist/inspector/styleEditors/stylePresence.js +22 -0
- package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
- package/dist/inspector/styleEditors/textRelevance.js +35 -0
- package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
- package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
- package/dist/inspector/tokens/TokenField.js +130 -7
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
- package/dist/inspector/tokens/dragPointerLock.js +70 -0
- package/dist/inspector/tokens/editActions.d.ts +2 -0
- package/dist/inspector/tokens/editActions.js +2 -2
- package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
- package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
- package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
- package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
- package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
- package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
- package/dist/inspector/tokens/resolution.d.ts +3 -26
- package/dist/inspector/tokens/resolution.js +44 -735
- package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
- package/dist/inspector/tokens/tokenAvailability.js +204 -0
- package/dist/inspector/ui/Button.d.ts +1 -1
- package/dist/inspector/ui/Disclosure.d.ts +22 -0
- package/dist/inspector/ui/Disclosure.js +13 -0
- package/dist/inspector/ui/SideValuesField.js +2 -2
- package/dist/inspector/ui/StatusCallout.d.ts +4 -1
- package/dist/inspector/ui/StatusCallout.js +2 -2
- package/dist/inspector/ui/dragCursor.d.ts +8 -0
- package/dist/inspector/ui/dragCursor.js +21 -0
- package/dist/inspector/ui/styles.js +21 -17
- package/dist/internal/agent-protocol.d.ts +65 -1
- package/dist/internal/agent-protocol.js +183 -3
- package/dist/nudge-ui.mjs +276 -58
- package/dist/transport/editor.d.ts +47 -0
- package/dist/transport/editor.js +163 -0
- package/dist/transport/index.d.ts +1 -0
- package/dist/transport/index.js +1 -0
- package/dist/transport/routes.d.ts +10 -0
- package/dist/transport/routes.js +10 -0
- package/package.json +3 -1
- package/dist/inspector/shell/DomNavigation.d.ts +0 -8
- package/dist/inspector/shell/DomNavigation.js +0 -30
- package/dist/inspector/shell/panelLayout.d.ts +0 -2
- package/dist/inspector/shell/panelLayout.js +0 -29
|
@@ -6,7 +6,9 @@ import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path
|
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { instrumentSourceHtml } from "../../html/identity.js";
|
|
8
8
|
import { injectStandaloneBootstrap } from "./html/bootstrap.js";
|
|
9
|
-
import {
|
|
9
|
+
import { createNudgeUiEditorDocument, isNudgeUiEditorDocumentRequest } from "../../transport/editor.js";
|
|
10
|
+
import { createStandaloneRuntimeManifest, NUDGE_UI_CLIENT_PATH, NUDGE_UI_EDITOR_PATH, NUDGE_UI_MANIFEST_PATH, NUDGE_UI_RELOAD_PATH, NUDGE_UI_ROUTE_PREFIX, } from "./manifest.js";
|
|
11
|
+
import { startOptionalProjectBridge } from "../projectBridge.js";
|
|
10
12
|
import { createProjectTokenSnapshot, EMPTY_PROJECT_TOKEN_SNAPSHOT } from "../../project/tokens.js";
|
|
11
13
|
import { createProjectFileWatcher, isSensitiveProjectPath, } from "../../project/files.js";
|
|
12
14
|
const LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1", "localhost"]);
|
|
@@ -36,6 +38,7 @@ export function createStandaloneServer(options) {
|
|
|
36
38
|
const projectId = options.projectId ?? createStandaloneProjectId(rootDirectory);
|
|
37
39
|
let revision = 0;
|
|
38
40
|
let manifest = createStandaloneRuntimeManifest(projectId, EMPTY_PROJECT_TOKEN_SNAPSHOT, revision);
|
|
41
|
+
let projectBridge = null;
|
|
39
42
|
// The initial scan runs asynchronously; start() awaits it so the first
|
|
40
43
|
// request and manifest always observe the complete project token knowledge.
|
|
41
44
|
const initialTokens = createProjectTokenSnapshot({ rootDirectory, generationLabel: "static-html" }).then((tokens) => {
|
|
@@ -78,7 +81,10 @@ export function createStandaloneServer(options) {
|
|
|
78
81
|
// The scan is asynchronous and bounded to the project root; rebuilding
|
|
79
82
|
// once per settled batch keeps the manifest coherent before reload.
|
|
80
83
|
const tokens = await createProjectTokenSnapshot({ rootDirectory, generationLabel: "static-html" });
|
|
81
|
-
manifest =
|
|
84
|
+
manifest = {
|
|
85
|
+
...createStandaloneRuntimeManifest(projectId, tokens, revision + 1),
|
|
86
|
+
...(projectBridge ? { agentBridge: projectBridge.browser } : {}),
|
|
87
|
+
};
|
|
82
88
|
revision += 1;
|
|
83
89
|
notifyReload();
|
|
84
90
|
}).catch((error) => {
|
|
@@ -146,7 +152,23 @@ export function createStandaloneServer(options) {
|
|
|
146
152
|
rejectStart(new Error("Standalone server closed during startup."));
|
|
147
153
|
return;
|
|
148
154
|
}
|
|
149
|
-
|
|
155
|
+
const address = readAddress(httpServer, host);
|
|
156
|
+
void startOptionalProjectBridge({
|
|
157
|
+
appRoot: rootDirectory,
|
|
158
|
+
projectId,
|
|
159
|
+
origin: address.url.replace(/\/$/, ""),
|
|
160
|
+
info: (message) => console.info(message),
|
|
161
|
+
warn: (message) => console.warn(message),
|
|
162
|
+
}).then((bridge) => {
|
|
163
|
+
projectBridge = bridge;
|
|
164
|
+
if (bridge)
|
|
165
|
+
manifest = { ...manifest, agentBridge: bridge.browser };
|
|
166
|
+
if (closeRequested) {
|
|
167
|
+
rejectStart(new Error("Standalone server closed during startup."));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
resolveStart(address);
|
|
171
|
+
});
|
|
150
172
|
});
|
|
151
173
|
};
|
|
152
174
|
httpServer.once("error", onError);
|
|
@@ -165,6 +187,10 @@ export function createStandaloneServer(options) {
|
|
|
165
187
|
closeReloadClients();
|
|
166
188
|
const closing = closeWatcher
|
|
167
189
|
.then(() => pendingStart?.catch(() => undefined))
|
|
190
|
+
.then(async () => {
|
|
191
|
+
await projectBridge?.close();
|
|
192
|
+
projectBridge = null;
|
|
193
|
+
})
|
|
168
194
|
.then(() => changeQueue)
|
|
169
195
|
.then(() => {
|
|
170
196
|
if (!httpServer.listening)
|
|
@@ -370,6 +396,13 @@ async function handleRequest(input) {
|
|
|
370
396
|
await handleNudgeUiRoute(input, decodedPath);
|
|
371
397
|
return;
|
|
372
398
|
}
|
|
399
|
+
if (isNudgeUiEditorDocumentRequest(request.url ?? "/", request.method, request.headers)) {
|
|
400
|
+
const body = request.method === "HEAD"
|
|
401
|
+
? Buffer.alloc(0)
|
|
402
|
+
: Buffer.from(createNudgeUiEditorDocument(), "utf8");
|
|
403
|
+
sendBody(response, 200, body, "text/html; charset=utf-8", { "Cache-Control": "no-store" });
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
373
406
|
const resolution = resolveDecodedStaticFile(input.rootDirectory, decodedPath);
|
|
374
407
|
if (!resolution) {
|
|
375
408
|
sendText(response, 404, "Not Found");
|
|
@@ -434,6 +467,15 @@ async function handleNudgeUiRoute(input, pathname) {
|
|
|
434
467
|
});
|
|
435
468
|
return;
|
|
436
469
|
}
|
|
470
|
+
if (pathname === NUDGE_UI_EDITOR_PATH) {
|
|
471
|
+
const body = input.request.method === "HEAD"
|
|
472
|
+
? Buffer.alloc(0)
|
|
473
|
+
: Buffer.from(createNudgeUiEditorDocument(), "utf8");
|
|
474
|
+
sendBody(input.response, 200, body, "text/html; charset=utf-8", {
|
|
475
|
+
"Cache-Control": "no-store",
|
|
476
|
+
});
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
437
479
|
if (pathname === NUDGE_UI_CLIENT_PATH) {
|
|
438
480
|
try {
|
|
439
481
|
const body = await readFileFromDescriptor(input.clientPath ?? resolveDefaultClientPath());
|
|
@@ -18,11 +18,16 @@ export interface VanillaExtractOptions {
|
|
|
18
18
|
source?: string;
|
|
19
19
|
}
|
|
20
20
|
export interface NudgeUiOptions extends ReactOptions {
|
|
21
|
+
/** `false` registers nothing. `NUDGE_UI=0` has the same effect for one run. */
|
|
21
22
|
enabled?: boolean;
|
|
22
23
|
/** Enables experimental DOM parent/child navigation in the Inspector. */
|
|
23
24
|
debug?: boolean;
|
|
24
|
-
/**
|
|
25
|
+
/** Enables the landing app's restricted, iframe-backed demo runtime. */
|
|
25
26
|
demo?: boolean;
|
|
27
|
+
/** Additional same-origin routes shown when the public demo opens. */
|
|
28
|
+
demoPages?: readonly string[];
|
|
29
|
+
/** Labels for the seeded demo cards in display order, including the primary card. */
|
|
30
|
+
demoCardLabels?: readonly string[];
|
|
26
31
|
/** Explicit project ID for browser-storage keys (defaults to root directory basename). */
|
|
27
32
|
projectId?: string;
|
|
28
33
|
/** Optional static v3 config for fixture/app integrations; dynamic configs are not executed. */
|
package/dist/hosts/vite/vite.js
CHANGED
|
@@ -2,11 +2,13 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { basename, join, relative, resolve, sep, } from "node:path";
|
|
4
4
|
import { createTokenInventory, } from "../../css/token-inventory/index.js";
|
|
5
|
-
import { NUDGE_UI_CLIENT_PATH, NUDGE_UI_MANIFEST_PATH } from "../../transport/index.js";
|
|
5
|
+
import { NUDGE_UI_CLIENT_PATH, NUDGE_UI_EDITOR_PATH, NUDGE_UI_MANIFEST_PATH, createNudgeUiEditorDocument, isNudgeUiEditorDocumentRequest, } from "../../transport/index.js";
|
|
6
6
|
import { discoverCssImportGraph, stripCssQuery } from "./tokens/activeStylesheets.js";
|
|
7
7
|
import { catalogSourcePath, createViteStylesheetArtifact, isHostApplicationSource, isPackageStylesheet, orderViteStylesheetGraph, } from "./tokens/viteStylesheetArtifacts.js";
|
|
8
8
|
import { interpretDialects } from "../../css/dialects/index.js";
|
|
9
9
|
import { detectStylingSystem, } from "../../inspector/clientManifest.js";
|
|
10
|
+
import { loopbackOrigin, startOptionalProjectBridge, } from "../projectBridge.js";
|
|
11
|
+
import { automationManifestFields, isNudgeUiEnabled } from "../environment.js";
|
|
10
12
|
function isRecord(value) {
|
|
11
13
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
14
|
}
|
|
@@ -100,12 +102,14 @@ function invalidateHmrModules(server, modules, timestamp) {
|
|
|
100
102
|
* here has grown a React assumption.
|
|
101
103
|
*/
|
|
102
104
|
export function createVitePlugins(options = {}, createFramework = null) {
|
|
103
|
-
const enabled = options.enabled
|
|
105
|
+
const enabled = isNudgeUiEnabled(options.enabled);
|
|
104
106
|
let root;
|
|
105
107
|
let buildOutputDirectory;
|
|
106
108
|
let command = "serve";
|
|
107
109
|
let demoBuild = false;
|
|
108
110
|
let devServer;
|
|
111
|
+
let projectBridge = null;
|
|
112
|
+
let projectBridgeStart = null;
|
|
109
113
|
let postTransformPromise = null;
|
|
110
114
|
const inventory = createTokenInventory();
|
|
111
115
|
const activeHostCssFiles = new Set();
|
|
@@ -473,14 +477,43 @@ export function createVitePlugins(options = {}, createFramework = null) {
|
|
|
473
477
|
return postTransformPromise;
|
|
474
478
|
}
|
|
475
479
|
async function buildClientManifest() {
|
|
480
|
+
await projectBridgeStart;
|
|
476
481
|
await ensurePostTransformCss();
|
|
477
482
|
await ensurePublishedThemeContract();
|
|
478
483
|
return {
|
|
479
484
|
version: 1,
|
|
480
485
|
revision: 0,
|
|
481
486
|
runtime: buildRuntimeSnapshot(),
|
|
487
|
+
...(projectBridge ? { agentBridge: projectBridge.browser } : {}),
|
|
488
|
+
...automationManifestFields(),
|
|
482
489
|
};
|
|
483
490
|
}
|
|
491
|
+
function startProjectBridge(server) {
|
|
492
|
+
// The public demo uses the shared editor runtime without project-owned
|
|
493
|
+
// sessions, an agent bridge, or filesystem persistence.
|
|
494
|
+
if (options.demo === true || !root || projectBridgeStart)
|
|
495
|
+
return;
|
|
496
|
+
const address = server.httpServer?.address();
|
|
497
|
+
if (!address || typeof address === "string")
|
|
498
|
+
return;
|
|
499
|
+
const origin = server.resolvedUrls?.local[0]
|
|
500
|
+
? new URL(server.resolvedUrls.local[0]).origin
|
|
501
|
+
: loopbackOrigin(address, server.config.server.https ? "https" : "http");
|
|
502
|
+
const allowedOrigins = [
|
|
503
|
+
...(server.resolvedUrls?.local ?? []),
|
|
504
|
+
...(server.resolvedUrls?.network ?? []),
|
|
505
|
+
].map((url) => new URL(url).origin);
|
|
506
|
+
projectBridgeStart = startOptionalProjectBridge({
|
|
507
|
+
appRoot: root,
|
|
508
|
+
projectId: options.projectId ?? basename(root),
|
|
509
|
+
origin,
|
|
510
|
+
...(allowedOrigins.length > 0 ? { allowedOrigins } : {}),
|
|
511
|
+
info: (message) => server.config.logger.info(message),
|
|
512
|
+
warn: (message) => server.config.logger.warn(message),
|
|
513
|
+
}).then((bridge) => {
|
|
514
|
+
projectBridge = bridge;
|
|
515
|
+
});
|
|
516
|
+
}
|
|
484
517
|
function buildRuntimeSnapshot() {
|
|
485
518
|
syncInventoryContributions();
|
|
486
519
|
const snapshot = inventory.snapshot();
|
|
@@ -497,6 +530,9 @@ export function createVitePlugins(options = {}, createFramework = null) {
|
|
|
497
530
|
host: framework?.hostLabel ?? "static-html",
|
|
498
531
|
framework: framework?.framework ?? "HTML",
|
|
499
532
|
stylingSystem: detectStylingSystem(snapshot.tokens),
|
|
533
|
+
...(options.demo === true ? { demo: true } : {}),
|
|
534
|
+
...(options.demoPages === undefined ? {} : { demoPages: options.demoPages }),
|
|
535
|
+
...(options.demoCardLabels === undefined ? {} : { demoCardLabels: options.demoCardLabels }),
|
|
500
536
|
capabilities: { canvas: true, componentSemantics: framework !== null },
|
|
501
537
|
tokenCatalog: snapshot.definitions.map((definition) => ({
|
|
502
538
|
...definition,
|
|
@@ -536,16 +572,38 @@ export function createVitePlugins(options = {}, createFramework = null) {
|
|
|
536
572
|
if (!enabled || command !== "serve")
|
|
537
573
|
return;
|
|
538
574
|
devServer = server;
|
|
575
|
+
if (server.httpServer?.listening)
|
|
576
|
+
startProjectBridge(server);
|
|
577
|
+
else
|
|
578
|
+
server.httpServer?.once("listening", () => startProjectBridge(server));
|
|
579
|
+
server.httpServer?.once("close", () => {
|
|
580
|
+
void projectBridgeStart?.then(() => projectBridge?.close()).finally(() => {
|
|
581
|
+
projectBridge = null;
|
|
582
|
+
projectBridgeStart = null;
|
|
583
|
+
});
|
|
584
|
+
});
|
|
539
585
|
const runtimeWarning = framework?.unresolvedRuntimeWarning();
|
|
540
586
|
if (runtimeWarning)
|
|
541
587
|
server.config.logger.warn(runtimeWarning);
|
|
542
588
|
server.middlewares?.use(async (request, response, next) => {
|
|
543
589
|
const pathname = new URL(request.url ?? "/", "http://nudge-ui.local").pathname;
|
|
544
|
-
|
|
590
|
+
const invalidReservedMethod = (pathname === CLIENT_PATH || pathname === MANIFEST_PATH)
|
|
591
|
+
? request.method !== "GET"
|
|
592
|
+
: pathname === NUDGE_UI_EDITOR_PATH
|
|
593
|
+
&& request.method !== "GET"
|
|
594
|
+
&& request.method !== "HEAD";
|
|
595
|
+
if (invalidReservedMethod) {
|
|
545
596
|
response.statusCode = 405;
|
|
546
597
|
response.end();
|
|
547
598
|
return;
|
|
548
599
|
}
|
|
600
|
+
if (isNudgeUiEditorDocumentRequest(request.url ?? "/", request.method, request.headers)) {
|
|
601
|
+
response.statusCode = 200;
|
|
602
|
+
response.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
603
|
+
response.setHeader("Cache-Control", "no-store");
|
|
604
|
+
response.end(request.method === "HEAD" ? undefined : createNudgeUiEditorDocument());
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
549
607
|
if (pathname === CLIENT_PATH) {
|
|
550
608
|
try {
|
|
551
609
|
response.statusCode = 200;
|
|
@@ -628,29 +686,39 @@ export function createVitePlugins(options = {}, createFramework = null) {
|
|
|
628
686
|
return `export {};\n`;
|
|
629
687
|
}
|
|
630
688
|
if (options.demo === true) {
|
|
631
|
-
const
|
|
689
|
+
const demoControllerExpression = demoBuild
|
|
632
690
|
? "true"
|
|
633
691
|
: '(import.meta.env.DEV && window.location.pathname === "/")';
|
|
634
692
|
// Taken from the composed framework, so the demo bootstrap cannot drift
|
|
635
693
|
// from the manifest the transport serves.
|
|
636
694
|
const identity = buildRuntimeSnapshot();
|
|
637
695
|
return [
|
|
638
|
-
'import { bootstrapNudgeUi, configureNudgeUiRuntime, detectFramework,
|
|
696
|
+
'import { bootstrapNudgeUi, configureNudgeUiRuntime, createNudgeUiEditorUrl, detectFramework, hasNudgeUiDirectTabIntent, isCanvasRenderer, isNudgeUiDirectUrl, readNudgeUiEditorTarget, rememberNudgeUiDirectTabIntent } from "nudge-ui/internal/inspector";',
|
|
639
697
|
'import { tokenCatalog, tokens, tokenDiagnostics, tokenGeneration, nudgeUiProjectId } from "virtual:design-tokens";',
|
|
640
698
|
...(framework
|
|
641
699
|
? [`import { componentContracts } from ${JSON.stringify(framework.virtualModuleId)};`]
|
|
642
700
|
: []),
|
|
643
|
-
'const
|
|
644
|
-
|
|
645
|
-
'
|
|
646
|
-
'
|
|
701
|
+
'const __nudge_ui_renderer = isCanvasRenderer();',
|
|
702
|
+
'const __nudge_ui_explicit_direct = isNudgeUiDirectUrl(window.location.href);',
|
|
703
|
+
'if (__nudge_ui_explicit_direct) rememberNudgeUiDirectTabIntent();',
|
|
704
|
+
'const __nudge_ui_direct_tab = hasNudgeUiDirectTabIntent();',
|
|
705
|
+
`const __nudge_ui_demo_controller = !__nudge_ui_renderer && !__nudge_ui_explicit_direct && !__nudge_ui_direct_tab && ${demoControllerExpression};`,
|
|
706
|
+
'const __nudge_ui_editor_target = readNudgeUiEditorTarget(window.location.href) ?? (__nudge_ui_demo_controller ? window.location.href : null);',
|
|
707
|
+
'if (__nudge_ui_demo_controller && readNudgeUiEditorTarget(window.location.href) === null) {',
|
|
708
|
+
' window.history.replaceState(window.history.state, "", createNudgeUiEditorUrl(window.location.href));',
|
|
709
|
+
'}',
|
|
710
|
+
'if (__nudge_ui_editor_target) document.documentElement.setAttribute("data-nudge-ui-editor", "");',
|
|
711
|
+
'if (__nudge_ui_renderer) document.documentElement.setAttribute("data-nudge-ui-renderer", "");',
|
|
712
|
+
'if (__nudge_ui_editor_target || __nudge_ui_renderer) {',
|
|
647
713
|
' configureNudgeUiRuntime({',
|
|
648
714
|
' projectId: nudgeUiProjectId,',
|
|
649
715
|
` host: ${JSON.stringify(identity.host)},`,
|
|
650
716
|
` framework: ${JSON.stringify(identity.framework)},`,
|
|
651
717
|
' stylingSystem: detectFramework(tokens).stylingSystem,',
|
|
652
|
-
'
|
|
653
|
-
`
|
|
718
|
+
' demo: true,',
|
|
719
|
+
` demoPages: ${JSON.stringify(options.demoPages ?? [])},`,
|
|
720
|
+
` demoCardLabels: ${JSON.stringify(options.demoCardLabels ?? [])},`,
|
|
721
|
+
` capabilities: { canvas: true, componentSemantics: ${String(framework !== null)} },`,
|
|
654
722
|
' tokenCatalog,',
|
|
655
723
|
' tokens,',
|
|
656
724
|
' tokenDiagnostics,',
|
|
@@ -660,12 +728,11 @@ export function createVitePlugins(options = {}, createFramework = null) {
|
|
|
660
728
|
' const __dt_root = document.getElementById("nudge-ui-root");',
|
|
661
729
|
' if (__dt_root) {',
|
|
662
730
|
' bootstrapNudgeUi(__dt_root);',
|
|
663
|
-
' if (__nudge_ui_demo_runtime) {',
|
|
664
|
-
' setInspectorOpen(false);',
|
|
665
|
-
' window.addEventListener("nudge-ui:open", () => setInspectorOpen(true));',
|
|
666
|
-
' }',
|
|
667
731
|
' }',
|
|
668
732
|
'}',
|
|
733
|
+
'window.addEventListener("nudge-ui:open", () => {',
|
|
734
|
+
' if (!__nudge_ui_renderer && (__nudge_ui_explicit_direct || __nudge_ui_direct_tab)) window.location.assign(createNudgeUiEditorUrl(window.location.href));',
|
|
735
|
+
'});',
|
|
669
736
|
].join("\n");
|
|
670
737
|
}
|
|
671
738
|
return null;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
2
|
import type { AgentClientSnapshot } from "./client.js";
|
|
3
3
|
export declare const MCP_DOCS_URL = "https://github.com/charlessmart/nudge-ui#connect-a-coding-agent";
|
|
4
|
-
/**
|
|
5
|
-
export declare function createMcpSetupCommand(
|
|
4
|
+
/** Uses the guided installer so browser diagnostics never become shell arguments. */
|
|
5
|
+
export declare function createMcpSetupCommand(): string;
|
|
6
6
|
export interface McpConnectionContentProps {
|
|
7
7
|
readonly projectId: string;
|
|
8
8
|
readonly origin: string;
|
|
9
9
|
readonly snapshot: AgentClientSnapshot;
|
|
10
10
|
readonly onConnect: () => Promise<boolean> | boolean | void;
|
|
11
|
+
readonly onTakeOver: () => Promise<boolean> | boolean | void;
|
|
11
12
|
readonly onDisconnect: () => void;
|
|
12
13
|
readonly onCheckAgain: () => Promise<void>;
|
|
13
14
|
}
|
|
@@ -16,11 +17,11 @@ export interface McpConnectionDialogProps extends McpConnectionContentProps {
|
|
|
16
17
|
readonly onOpenChange: (open: boolean) => void;
|
|
17
18
|
}
|
|
18
19
|
/** Renders the MCP setup and pairing flow without owning a dialog surface. */
|
|
19
|
-
export declare function McpConnectionContent({ projectId, origin, snapshot, onConnect, onDisconnect, onCheckAgain, }: McpConnectionContentProps): ReactElement;
|
|
20
|
+
export declare function McpConnectionContent({ projectId, origin, snapshot, onConnect, onTakeOver, onDisconnect, onCheckAgain, }: McpConnectionContentProps): ReactElement;
|
|
20
21
|
/**
|
|
21
22
|
* Explains and controls the project-scoped MCP companion connection.
|
|
22
23
|
*
|
|
23
24
|
* This compatibility wrapper keeps the existing standalone MCP surface while
|
|
24
25
|
* the shared content is also embedded in the general settings modal.
|
|
25
26
|
*/
|
|
26
|
-
export declare function McpConnectionDialog({ open, projectId, origin, snapshot, onOpenChange, onConnect, onDisconnect, onCheckAgain, }: McpConnectionDialogProps): ReactElement;
|
|
27
|
+
export declare function McpConnectionDialog({ open, projectId, origin, snapshot, onOpenChange, onConnect, onTakeOver, onDisconnect, onCheckAgain, }: McpConnectionDialogProps): ReactElement;
|
|
@@ -5,35 +5,13 @@ import { IconCheck, IconClipboard, IconExternalLink, IconPlugConnected, IconRefr
|
|
|
5
5
|
import { getAgentConnectionStatus } from "./connectionStatus.js";
|
|
6
6
|
import { copyToClipboard } from "../prompt/copyToClipboard.js";
|
|
7
7
|
import { Button } from "../ui/Button.js";
|
|
8
|
+
import { Disclosure } from "../ui/Disclosure.js";
|
|
8
9
|
import { SegmentedControl } from "../ui/SegmentedControl.js";
|
|
9
10
|
import { StatusCallout } from "../ui/StatusCallout.js";
|
|
10
11
|
export const MCP_DOCS_URL = "https://github.com/charlessmart/nudge-ui#connect-a-coding-agent";
|
|
11
|
-
/**
|
|
12
|
-
function
|
|
13
|
-
return
|
|
14
|
-
}
|
|
15
|
-
function shellArgument(value) {
|
|
16
|
-
// Keep the usual command readable while quoting values that could be
|
|
17
|
-
// interpreted as shell syntax when add-mcp runs the nested command.
|
|
18
|
-
return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : shellQuote(value);
|
|
19
|
-
}
|
|
20
|
-
/** Builds the project-local add-mcp command shown by the connection panel. */
|
|
21
|
-
export function createMcpSetupCommand(projectId, origin) {
|
|
22
|
-
const serverCommand = [
|
|
23
|
-
"npx -y @nudge-ui/mcp@latest",
|
|
24
|
-
"--project-id",
|
|
25
|
-
shellArgument(projectId),
|
|
26
|
-
"--origin",
|
|
27
|
-
shellArgument(origin),
|
|
28
|
-
"--workspace-root .",
|
|
29
|
-
].join(" ");
|
|
30
|
-
return `npx add-mcp ${shellQuote(serverCommand)} --name nudge_ui`;
|
|
31
|
-
}
|
|
32
|
-
function formatMcpSetupCommand(command) {
|
|
33
|
-
return command
|
|
34
|
-
.replace(" --project-id ", "\n --project-id ")
|
|
35
|
-
.replace(" --origin ", "\n --origin ")
|
|
36
|
-
.replace(" --workspace-root ", "\n --workspace-root ");
|
|
12
|
+
/** Uses the guided installer so browser diagnostics never become shell arguments. */
|
|
13
|
+
export function createMcpSetupCommand() {
|
|
14
|
+
return "npx nudge-ui agent setup";
|
|
37
15
|
}
|
|
38
16
|
function portalContainer() {
|
|
39
17
|
return typeof document !== "undefined"
|
|
@@ -44,7 +22,7 @@ function TimelineStep({ number, complete, title, children, }) {
|
|
|
44
22
|
return (_jsxs("li", { className: `mcp-connection__timeline-step${complete ? " mcp-connection__timeline-step--complete" : ""}`, "data-test": `mcp-step-${number}`, "data-complete": complete ? "true" : "false", children: [_jsx("div", { className: "mcp-connection__timeline-marker", "aria-hidden": "true", children: complete ? _jsx(IconCheck, { size: "var(--icon-size-small)", stroke: 2 }) : number }), _jsxs("section", { className: "mcp-connection__timeline-content", children: [_jsx("h3", { className: "mcp-connection__section-title", children: title }), children] })] }));
|
|
45
23
|
}
|
|
46
24
|
/** Renders the MCP setup and pairing flow without owning a dialog surface. */
|
|
47
|
-
export function McpConnectionContent({ projectId, origin, snapshot, onConnect, onDisconnect, onCheckAgain, }) {
|
|
25
|
+
export function McpConnectionContent({ projectId, origin, snapshot, onConnect, onTakeOver, onDisconnect, onCheckAgain, }) {
|
|
48
26
|
const [checking, setChecking] = useState(false);
|
|
49
27
|
const [setupMethod, setSetupMethod] = useState("ai");
|
|
50
28
|
const [copiedCommand, setCopiedCommand] = useState(false);
|
|
@@ -52,24 +30,29 @@ export function McpConnectionContent({ projectId, origin, snapshot, onConnect, o
|
|
|
52
30
|
const [copiedListener, setCopiedListener] = useState(false);
|
|
53
31
|
const [copyError, setCopyError] = useState();
|
|
54
32
|
const [checkError, setCheckError] = useState();
|
|
55
|
-
const setupCommand =
|
|
56
|
-
const displaySetupCommand = useMemo(() => formatMcpSetupCommand(setupCommand), [setupCommand]);
|
|
33
|
+
const setupCommand = createMcpSetupCommand();
|
|
57
34
|
const setupPrompt = useMemo(() => [
|
|
58
|
-
"Please
|
|
35
|
+
"Please set up the Nudge coding-agent integration for this application.",
|
|
59
36
|
"",
|
|
60
|
-
"Run this command from the
|
|
37
|
+
"Run this command from the application directory and select this coding agent:",
|
|
61
38
|
setupCommand,
|
|
62
39
|
"",
|
|
63
|
-
"
|
|
40
|
+
"Start the application normally. Reload the agent if needed, then listen to Nudge.",
|
|
64
41
|
].join("\n"), [setupCommand]);
|
|
65
|
-
const listenerInstruction = "
|
|
42
|
+
const listenerInstruction = "Listen to Nudge for this application in the current worktree. Use nudge_list_sessions if selection is ambiguous, then call nudge_listen. After applying each prompt, call nudge_report_status and listen again until I ask you to stop.";
|
|
66
43
|
const status = getAgentConnectionStatus(snapshot);
|
|
67
44
|
const canConnect = snapshot.companionReachable
|
|
68
45
|
&& !snapshot.paired
|
|
46
|
+
&& !snapshot.pairedElsewhere
|
|
69
47
|
&& snapshot.state !== "pairing"
|
|
70
48
|
&& snapshot.state !== "working"
|
|
71
49
|
&& snapshot.request?.status !== "working";
|
|
72
50
|
const canDisconnect = snapshot.paired || snapshot.request?.status === "working";
|
|
51
|
+
const canTakeOver = snapshot.companionReachable
|
|
52
|
+
&& snapshot.pairedElsewhere
|
|
53
|
+
&& snapshot.state !== "pairing"
|
|
54
|
+
&& snapshot.state !== "working"
|
|
55
|
+
&& snapshot.request?.status !== "working";
|
|
73
56
|
async function handleCheckAgain() {
|
|
74
57
|
if (checking)
|
|
75
58
|
return;
|
|
@@ -118,10 +101,10 @@ export function McpConnectionContent({ projectId, origin, snapshot, onConnect, o
|
|
|
118
101
|
setCopyError("The listener instruction could not be copied. Select it and copy it manually.");
|
|
119
102
|
}
|
|
120
103
|
}
|
|
121
|
-
return (_jsxs(_Fragment, { children: [_jsxs(StatusCallout, { className: "mcp-connection__status", tone: status.tone, "data-test": "mcp-connection-status", children: [_jsx("span", { className: "mcp-connection__status-label", role: "status", children: status.label }), snapshot.error ? (_jsx("span", { className: "mcp-connection__status-detail", children: snapshot.error })) : null, checkError ? _jsx("span", { role: "alert", children: checkError }) : null, snapshot.request?.summary ? (_jsx("span", { className: "mcp-connection__status-detail", children: snapshot.request.summary })) : null, snapshot.request?.error ? (_jsx("span", { className: "mcp-connection__status-detail", children: snapshot.request.error })) : null] }), _jsxs("ol", { className: "mcp-connection__timeline", "data-test": "mcp-connection-timeline", children: [_jsxs(TimelineStep, { number: 1, complete: snapshot.
|
|
104
|
+
return (_jsxs(_Fragment, { children: [_jsxs(StatusCallout, { className: "mcp-connection__status", tone: status.tone, "data-test": "mcp-connection-status", children: [_jsx("span", { className: "mcp-connection__status-label", role: "status", children: status.label }), snapshot.error ? (_jsx("span", { className: "mcp-connection__status-detail", children: snapshot.error })) : null, checkError ? _jsx("span", { role: "alert", children: checkError }) : null, snapshot.request?.summary ? (_jsx("span", { className: "mcp-connection__status-detail", children: snapshot.request.summary })) : null, snapshot.request?.error ? (_jsx("span", { className: "mcp-connection__status-detail", children: snapshot.request.error })) : null] }), _jsxs("ol", { className: "mcp-connection__timeline", "data-test": "mcp-connection-timeline", children: [_jsxs(TimelineStep, { number: 1, complete: snapshot.listenerActive || snapshot.request !== null, title: "Set up your agent once", children: [_jsx(SegmentedControl, { "aria-label": "MCP setup method", className: "mcp-connection__setup-tabs", "data-test": "mcp-setup-tabs", value: setupMethod, options: [
|
|
122
105
|
{ value: "ai", label: "AI instructions", testId: "mcp-setup-tab-ai" },
|
|
123
106
|
{ value: "terminal", label: "Terminal command", testId: "mcp-setup-tab-terminal" },
|
|
124
|
-
], onChange: setSetupMethod }), setupMethod === "terminal" ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "mcp-connection__copy", children: "Run this command from your
|
|
107
|
+
], onChange: setSetupMethod }), setupMethod === "terminal" ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "mcp-connection__copy", children: "Run this command from the application directory, then start your app normally." }), _jsx("pre", { className: "mcp-connection__command", children: _jsx("code", { "data-test": "mcp-setup-command", children: setupCommand }) }), _jsx("div", { className: "mcp-connection__command-actions", children: _jsxs(Button, { variant: "secondary", "data-test": "mcp-copy-command", type: "button", onClick: () => void handleCopyCommand(), children: [copiedCommand ? _jsx(IconCheck, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) : _jsx(IconClipboard, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), copiedCommand ? "Copied" : "Copy command"] }) })] })) : (_jsxs(_Fragment, { children: [_jsx("p", { className: "mcp-connection__copy", children: "Copy these setup instructions into your coding agent." }), _jsx("pre", { className: "mcp-connection__command", children: _jsx("code", { "data-test": "mcp-setup-prompt", children: setupPrompt }) }), _jsx("div", { className: "mcp-connection__command-actions", children: _jsxs(Button, { variant: "secondary", "data-test": "mcp-copy-setup-prompt", type: "button", onClick: () => void handleCopyPrompt(), children: [copiedPrompt ? _jsx(IconCheck, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) : _jsx(IconClipboard, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), copiedPrompt ? "Copied" : "Copy prompt"] }) })] }))] }), _jsxs(TimelineStep, { number: 2, complete: snapshot.listenerActive, title: "Listen in this agent session", children: [_jsx("p", { className: "mcp-connection__copy", children: "Tell your coding agent: \u201CListen to Nudge.\u201D Reload the agent if Nudge tools are unavailable." }), _jsx("div", { className: "mcp-connection__command-actions", children: _jsxs(Button, { variant: "secondary", "data-test": "mcp-copy-listener", type: "button", onClick: () => void handleCopyListener(), children: [copiedListener ? _jsx(IconCheck, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) : _jsx(IconClipboard, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), copiedListener ? "Copied" : "Copy listening instruction"] }) })] })] }), _jsxs(Disclosure, { className: "mcp-connection__details", title: "Connection details and recovery", children: [_jsxs("p", { className: "mcp-connection__copy", children: ["The page connects automatically when the development integration is available. To diagnose setup, run ", _jsx("code", { children: "nudge-ui agent doctor" }), " from the application directory."] }), _jsxs("dl", { className: "mcp-connection__diagnostics", "data-test": "mcp-connection-diagnostics", children: [_jsxs("div", { className: "mcp-connection__diagnostic", children: [_jsx("dt", { children: "Project ID" }), _jsx("dd", { "data-test": "mcp-project-id", children: _jsx("code", { children: projectId }) })] }), _jsxs("div", { className: "mcp-connection__diagnostic", children: [_jsx("dt", { children: "Origin" }), _jsx("dd", { "data-test": "mcp-origin", children: _jsx("code", { children: origin }) })] })] }), _jsxs("div", { className: "mcp-connection__actions", children: [canTakeOver ? (_jsxs(Button, { variant: "primary", "data-test": "mcp-takeover", type: "button", onClick: () => void onTakeOver(), children: [_jsx(IconPlugConnected, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), "Take over"] })) : null, canConnect ? (_jsxs(Button, { variant: "primary", "data-test": "mcp-connect", type: "button", onClick: () => void onConnect(), children: [_jsx(IconPlugConnected, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), "Connect"] })) : null, _jsxs(Button, { variant: "quiet", "data-test": "mcp-check-again", type: "button", disabled: checking || snapshot.state === "disabled" || snapshot.state === "pairing", onClick: () => void handleCheckAgain(), children: [_jsx(IconRefresh, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), checking ? "Checking…" : "Check again"] }), canDisconnect ? (_jsxs(Button, { variant: "danger", "data-test": "mcp-disconnect", type: "button", onClick: onDisconnect, children: [_jsx(IconUnlink, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), snapshot.paired ? "Disconnect" : "Forget connection"] })) : null] })] }), copyError ? _jsx("p", { className: "mcp-connection__error", role: "alert", children: copyError }) : null, _jsxs("a", { className: "mcp-connection__docs", "data-test": "mcp-docs-link", href: MCP_DOCS_URL, target: "_blank", rel: "noreferrer", children: [_jsx(IconExternalLink, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), "Read the MCP connection guide"] })] }));
|
|
125
108
|
}
|
|
126
109
|
/**
|
|
127
110
|
* Explains and controls the project-scoped MCP companion connection.
|
|
@@ -129,7 +112,7 @@ export function McpConnectionContent({ projectId, origin, snapshot, onConnect, o
|
|
|
129
112
|
* This compatibility wrapper keeps the existing standalone MCP surface while
|
|
130
113
|
* the shared content is also embedded in the general settings modal.
|
|
131
114
|
*/
|
|
132
|
-
export function McpConnectionDialog({ open, projectId, origin, snapshot, onOpenChange, onConnect, onDisconnect, onCheckAgain, }) {
|
|
115
|
+
export function McpConnectionDialog({ open, projectId, origin, snapshot, onOpenChange, onConnect, onTakeOver, onDisconnect, onCheckAgain, }) {
|
|
133
116
|
const closeRef = useRef(null);
|
|
134
|
-
return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { className: "mcp-connection__backdrop", "data-test": "mcp-connection-backdrop" }), _jsxs(Dialog.Popup, { className: "mcp-connection__popup", "data-test": "mcp-connection-dialog", initialFocus: closeRef, children: [_jsxs("div", { className: "mcp-connection__header", children: [_jsxs("div", { children: [_jsx(Dialog.Title, { className: "mcp-connection__title", children: "Connect MCP" }), _jsx(Dialog.Description, { className: "mcp-connection__description", children: "
|
|
117
|
+
return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { className: "mcp-connection__backdrop", "data-test": "mcp-connection-backdrop" }), _jsxs(Dialog.Popup, { className: "mcp-connection__popup", "data-test": "mcp-connection-dialog", initialFocus: closeRef, children: [_jsxs("div", { className: "mcp-connection__header", children: [_jsxs("div", { children: [_jsx(Dialog.Title, { className: "mcp-connection__title", children: "Connect MCP" }), _jsx(Dialog.Description, { className: "mcp-connection__description", children: "Set up your coding agent once, then ask it to listen to Nudge in this worktree." })] }), _jsx(Dialog.Close, { ref: closeRef, className: "icon-button icon-button--quiet mcp-connection__close", "aria-label": "Close MCP connection", "data-test": "mcp-connection-close", type: "button", children: _jsx(IconX, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) })] }), _jsx(McpConnectionContent, { projectId: projectId, origin: origin, snapshot: snapshot, onConnect: onConnect, onTakeOver: onTakeOver, onDisconnect: onDisconnect, onCheckAgain: onCheckAgain })] })] }) }));
|
|
135
118
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { type AgentBridgeTransport, type AgentClientOptions, type AgentRequestStatus, type AgentStatusSnapshot, type PromptDispatchResponse } from "./protocol.js";
|
|
1
|
+
import { type AgentBridgeTransport, type AgentClientOptions, type AgentSketchAttachment, type AgentSketchMetadata, type AgentRequestStatus, type AgentStatusSnapshot, type PromptDispatchResponse } from "./protocol.js";
|
|
2
2
|
export type AgentClientState = "disabled" | "disconnected" | "available" | "pairing" | "connected" | "working" | "completed" | "failed" | "interrupted";
|
|
3
3
|
export interface AgentRequestSnapshot {
|
|
4
4
|
readonly requestId: string;
|
|
5
5
|
readonly changeRevision?: number;
|
|
6
|
+
readonly clientDispatchId?: string;
|
|
7
|
+
readonly sketches?: readonly AgentSketchMetadata[];
|
|
6
8
|
readonly status: AgentRequestStatus;
|
|
7
9
|
readonly summary?: string;
|
|
8
10
|
readonly error?: string;
|
|
@@ -16,11 +18,13 @@ export interface AgentClientSnapshot {
|
|
|
16
18
|
/** Whether the last valid discovery response came from the local companion. */
|
|
17
19
|
readonly companionReachable: boolean;
|
|
18
20
|
readonly paired: boolean;
|
|
21
|
+
/** Whether another browser currently owns this project's pairing. */
|
|
22
|
+
readonly pairedElsewhere: boolean;
|
|
19
23
|
readonly request: AgentRequestSnapshot | null;
|
|
20
24
|
readonly error?: string;
|
|
21
25
|
}
|
|
22
26
|
/** Creates a stable numeric revision for one prompt snapshot. */
|
|
23
|
-
export declare function createPromptRevision(changes: readonly unknown[], structuralChanges?: readonly unknown[]): number;
|
|
27
|
+
export declare function createPromptRevision(changes: readonly unknown[], structuralChanges?: readonly unknown[], sketches?: readonly unknown[]): number;
|
|
24
28
|
/**
|
|
25
29
|
* Browser-side companion client.
|
|
26
30
|
*
|
|
@@ -49,7 +53,10 @@ export declare class AgentClient {
|
|
|
49
53
|
private listenerActive;
|
|
50
54
|
private companionReachable;
|
|
51
55
|
private paired;
|
|
56
|
+
private pairedElsewhere;
|
|
52
57
|
private lastError;
|
|
58
|
+
private readonly autoConnect;
|
|
59
|
+
private autoConnectSuppressed;
|
|
53
60
|
constructor(options: AgentClientOptions);
|
|
54
61
|
/** Installs the controller callback used to acknowledge Canvas commands. */
|
|
55
62
|
setCanvasCommandHandler(handler: AgentClientOptions["canvasCommandHandler"]): void;
|
|
@@ -66,13 +73,19 @@ export declare class AgentClient {
|
|
|
66
73
|
reconnect(): void;
|
|
67
74
|
/** Pairs the browser with a reachable companion, including an idle agent. */
|
|
68
75
|
connect(): Promise<boolean>;
|
|
76
|
+
/** Replaces an existing browser pairing after an explicit user action. */
|
|
77
|
+
takeOver(): Promise<boolean>;
|
|
78
|
+
private pairWith;
|
|
69
79
|
private openEvents;
|
|
70
80
|
/**
|
|
71
81
|
* Dispatches one immutable prompt revision. The request enters `working`
|
|
72
82
|
* synchronously, before the transport promise resolves, so the UI cannot
|
|
73
83
|
* issue a second prompt while the first one is in flight.
|
|
74
84
|
*/
|
|
75
|
-
dispatchPrompt(prompt: string, changeRevision?: number
|
|
85
|
+
dispatchPrompt(prompt: string, changeRevision?: number, options?: {
|
|
86
|
+
readonly clientDispatchId?: string;
|
|
87
|
+
readonly attachments?: readonly AgentSketchAttachment[];
|
|
88
|
+
}): Promise<PromptDispatchResponse | null>;
|
|
76
89
|
/** Interrupts local request state and asks the companion to revoke pairing. */
|
|
77
90
|
disconnect(reason?: string): void;
|
|
78
91
|
private discover;
|