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.
- package/README.md +164 -46
- package/dist/client.mjs +252 -54
- package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
- package/dist/css/value-semantics/colorSemantics.js +17 -0
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
- package/dist/hosts/astro/clientTransport.js +41 -32
- package/dist/hosts/astro/integration.d.ts +2 -2
- package/dist/hosts/astro/integration.js +9 -4
- package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
- package/dist/hosts/astro/markdownIdentity.js +81 -0
- package/dist/hosts/environment.d.ts +15 -0
- package/dist/hosts/environment.js +16 -0
- package/dist/hosts/next/manifest.js +1 -1
- package/dist/hosts/next/sidecar.js +12 -2
- package/dist/hosts/next/wrapper.d.ts +2 -0
- package/dist/hosts/next/wrapper.js +2 -1
- package/dist/hosts/projectBridge.d.ts +1 -0
- package/dist/hosts/projectBridge.js +1 -0
- package/dist/hosts/static/manifest.js +3 -1
- package/dist/hosts/static/server.js +4 -0
- package/dist/hosts/vite/vite.d.ts +4 -0
- package/dist/hosts/vite/vite.js +28 -5
- package/dist/inspector/agent/McpConnectionDialog.d.ts +3 -2
- package/dist/inspector/agent/McpConnectionDialog.js +23 -22
- package/dist/inspector/agent/activity.d.ts +18 -0
- package/dist/inspector/agent/activity.js +70 -0
- package/dist/inspector/agent/client.d.ts +8 -0
- package/dist/inspector/agent/client.js +65 -5
- package/dist/inspector/agent/connectionStatus.d.ts +3 -3
- package/dist/inspector/agent/connectionStatus.js +9 -2
- package/dist/inspector/agent/httpTransport.d.ts +6 -0
- package/dist/inspector/agent/httpTransport.js +50 -3
- package/dist/inspector/agent/protocol.d.ts +9 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +11 -1
- package/dist/inspector/agent/verification.js +53 -25
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
- package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
- package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
- package/dist/inspector/canvas/CanvasCard.js +133 -38
- package/dist/inspector/canvas/CanvasElementOverlay.js +200 -22
- package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
- package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
- package/dist/inspector/canvas/CanvasToolbar.js +8 -4
- package/dist/inspector/canvas/CanvasWorkspace.js +243 -56
- package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
- package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
- package/dist/inspector/canvas/agentPresentation.js +64 -52
- package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
- package/dist/inspector/canvas/canvasGestures.js +1 -1
- package/dist/inspector/canvas/canvasStore.d.ts +46 -82
- package/dist/inspector/canvas/canvasStore.js +251 -284
- package/dist/inspector/canvas/frameContent.d.ts +21 -0
- package/dist/inspector/canvas/frameContent.js +44 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +34 -4
- package/dist/inspector/canvas/frameProtocol.js +9 -2
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -1
- package/dist/inspector/canvas/keyboardShortcuts.js +5 -3
- package/dist/inspector/canvas/layout.d.ts +52 -0
- package/dist/inspector/canvas/layout.js +106 -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 +15 -5
- package/dist/inspector/canvas/projection.js +69 -13
- package/dist/inspector/canvas/rendererBootstrap.js +45 -4
- package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
- package/dist/inspector/canvas/rendererElementSelector.js +102 -45
- package/dist/inspector/canvas/rendererStylesheet.js +2 -2
- package/dist/inspector/canvas/resizeHandleScale.js +1 -1
- package/dist/inspector/canvas/sessionStore.d.ts +11 -26
- package/dist/inspector/canvas/sessionStore.js +43 -183
- package/dist/inspector/canvas/staleChangeDetector.js +9 -3
- package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
- package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
- package/dist/inspector/canvas/viewStore.d.ts +38 -0
- package/dist/inspector/canvas/viewStore.js +104 -0
- package/dist/inspector/canvas/wheelPan.d.ts +8 -0
- package/dist/inspector/canvas/wheelPan.js +8 -0
- package/dist/inspector/changes/changesLog.d.ts +6 -5
- package/dist/inspector/changes/changesLog.js +40 -32
- package/dist/inspector/changes/draftChanges.d.ts +56 -0
- package/dist/inspector/changes/draftChanges.js +300 -0
- package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
- package/dist/inspector/changes/previewDiagnostics.js +11 -25
- package/dist/inspector/clientManifest.d.ts +3 -1
- package/dist/inspector/clientManifest.js +1 -0
- package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
- package/dist/inspector/comments/CommentsOverlay.js +250 -0
- package/dist/inspector/comments/element.d.ts +6 -0
- package/dist/inspector/comments/element.js +47 -0
- package/dist/inspector/comments/store.d.ts +29 -0
- package/dist/inspector/comments/store.js +121 -0
- package/dist/inspector/comments/verification.d.ts +5 -0
- package/dist/inspector/comments/verification.js +52 -0
- package/dist/inspector/drafts/model.d.ts +35 -0
- package/dist/inspector/drafts/model.js +45 -0
- package/dist/inspector/drafts/store.d.ts +28 -0
- package/dist/inspector/drafts/store.js +231 -0
- package/dist/inspector/index.js +26 -35
- package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
- package/dist/inspector/iterations/capture.d.ts +2 -0
- package/dist/inspector/iterations/capture.js +173 -0
- package/dist/inspector/iterations/client.d.ts +7 -0
- package/dist/inspector/iterations/client.js +52 -0
- package/dist/inspector/iterations/coordinator.d.ts +15 -0
- package/dist/inspector/iterations/coordinator.js +130 -0
- package/dist/inspector/iterations/prompt.d.ts +3 -0
- package/dist/inspector/iterations/prompt.js +26 -0
- package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
- package/dist/inspector/overlay/DomNavigator.js +46 -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/interactionStyles.js +3 -1
- package/dist/inspector/overlay/spacingGestures.d.ts +15 -3
- package/dist/inspector/overlay/spacingGestures.js +88 -13
- package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
- package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
- package/dist/inspector/projection/renderedInstance.d.ts +2 -1
- package/dist/inspector/projection/renderedInstance.js +9 -5
- package/dist/inspector/projection/structuralProjection.d.ts +2 -1
- package/dist/inspector/projection/structuralProjection.js +19 -15
- package/dist/inspector/projection/textProjection.d.ts +2 -1
- package/dist/inspector/projection/textProjection.js +8 -4
- package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
- package/dist/inspector/prompt/clipboardHandoff.d.ts +10 -18
- package/dist/inspector/prompt/clipboardHandoff.js +92 -143
- package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
- package/dist/inspector/prompt/generatePrompt.js +17 -16
- package/dist/inspector/runtime/runtimeConfig.d.ts +10 -1
- package/dist/inspector/runtime/runtimeConfig.js +24 -0
- package/dist/inspector/selection/hierarchy.d.ts +6 -0
- package/dist/inspector/selection/hierarchy.js +8 -0
- package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
- package/dist/inspector/settings/SettingsDialog.js +22 -9
- package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
- package/dist/inspector/shell/AgentStatusButton.js +36 -0
- package/dist/inspector/shell/AppShell.js +4 -2
- package/dist/inspector/shell/ChangesLog.js +47 -27
- package/dist/inspector/shell/CopyPromptButton.js +146 -90
- package/dist/inspector/shell/EmptyState.js +9 -8
- package/dist/inspector/shell/InspectorShell.js +14 -25
- package/dist/inspector/shell/PeekOriginalButton.js +24 -23
- package/dist/inspector/shell/originalPreview.js +5 -5
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +11 -0
- package/dist/inspector/sketch/SketchEditor.js +14 -14
- package/dist/inspector/sketch/SketchEntryButton.js +1 -1
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
- package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
- package/dist/inspector/sketch/SketchOverlay.js +7 -7
- package/dist/inspector/sketch/SketchWorkspace.js +18 -12
- package/dist/inspector/sketch/interaction.d.ts +1 -0
- package/dist/inspector/sketch/interaction.js +8 -4
- package/dist/inspector/sketch/model.d.ts +5 -0
- package/dist/inspector/sketch/model.js +6 -2
- package/dist/inspector/sketch/raster.d.ts +1 -2
- package/dist/inspector/sketch/raster.js +68 -4
- package/dist/inspector/sketch/store.js +48 -23
- package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
- package/dist/inspector/styleEditors/BorderEditor.js +49 -78
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
- package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
- package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
- package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
- package/dist/inspector/styleEditors/ColorPicker.js +10 -4
- package/dist/inspector/styleEditors/GapField.js +3 -5
- package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
- package/dist/inspector/styleEditors/GridChildSection.js +18 -9
- package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
- package/dist/inspector/styleEditors/GridPicker.js +8 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/GridValueField.js +19 -14
- package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
- package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
- package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
- package/dist/inspector/styleEditors/LayoutSection.js +32 -11
- package/dist/inspector/styleEditors/SpacingBox.js +16 -9
- package/dist/inspector/styleEditors/Typography.js +11 -3
- package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
- package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
- package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
- package/dist/inspector/styleEditors/gradientValue.js +114 -0
- package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
- package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
- package/dist/inspector/styleEditors/layoutValue.js +0 -42
- package/dist/inspector/styleEditors/nudgeValue.d.ts +4 -4
- package/dist/inspector/styleEditors/nudgeValue.js +41 -11
- package/dist/inspector/tokens/TokenField.d.ts +7 -0
- package/dist/inspector/tokens/TokenField.js +49 -22
- package/dist/inspector/tokens/TokenLabel.js +5 -14
- 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 +1 -1
- package/dist/inspector/tokens/editActions.js +1 -1
- package/dist/inspector/ui/AtRuleContext.js +4 -6
- package/dist/inspector/ui/Button.d.ts +6 -2
- package/dist/inspector/ui/Button.js +4 -3
- package/dist/inspector/ui/ControlSurface.d.ts +2 -2
- package/dist/inspector/ui/ControlSurface.js +4 -3
- package/dist/inspector/ui/Disclosure.d.ts +22 -0
- package/dist/inspector/ui/Disclosure.js +13 -0
- package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
- package/dist/inspector/ui/InlineStyleWarning.js +13 -0
- package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
- package/dist/inspector/ui/InspectorPopover.js +4 -5
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +8 -7
- package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
- package/dist/inspector/ui/SegmentedControl.js +2 -2
- package/dist/inspector/ui/Select.d.ts +6 -1
- package/dist/inspector/ui/Select.js +7 -11
- package/dist/inspector/ui/SideValuesField.js +1 -1
- package/dist/inspector/ui/Tooltip.d.ts +26 -0
- package/dist/inspector/ui/Tooltip.js +32 -0
- package/dist/inspector/ui/classNames.d.ts +2 -0
- package/dist/inspector/ui/classNames.js +4 -0
- package/dist/inspector/ui/customCursors.d.ts +23 -0
- package/dist/inspector/ui/customCursors.js +122 -0
- package/dist/inspector/ui/dragCursor.d.ts +6 -0
- package/dist/inspector/ui/dragCursor.js +13 -0
- package/dist/inspector/ui/portalContainer.d.ts +2 -0
- package/dist/inspector/ui/portalContainer.js +6 -0
- package/dist/inspector/ui/styles.js +34 -24
- package/dist/inspector/workspace/commands.d.ts +9 -0
- package/dist/inspector/workspace/commands.js +82 -0
- package/dist/inspector/workspace/controller.d.ts +4 -0
- package/dist/inspector/workspace/controller.js +66 -0
- package/dist/inspector/workspace/handoff.d.ts +42 -0
- package/dist/inspector/workspace/handoff.js +115 -0
- package/dist/inspector/workspace/timeline.d.ts +30 -0
- package/dist/inspector/workspace/timeline.js +108 -0
- package/dist/internal/agent-protocol.d.ts +19 -2
- package/dist/internal/agent-protocol.js +13 -0
- package/dist/nudge-ui.mjs +560 -299
- package/dist/project/artifacts.d.ts +13 -0
- package/dist/project/artifacts.js +253 -0
- package/dist/transport/artifacts.d.ts +8 -0
- package/dist/transport/artifacts.js +9 -0
- package/dist/transport/editor.d.ts +20 -1
- package/dist/transport/editor.js +44 -11
- package/dist/transport/routes.d.ts +6 -1
- package/dist/transport/routes.js +6 -1
- package/package.json +6 -2
- package/dist/inspector/changes/workspaceChanges.d.ts +0 -43
- package/dist/inspector/changes/workspaceChanges.js +0 -239
- package/dist/inspector/shell/DomNavigation.d.ts +0 -8
- package/dist/inspector/shell/DomNavigation.js +0 -30
|
@@ -1,34 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { ChangeRecord } from "../changes/types.js";
|
|
2
|
+
import type { StructuralChange } from "../changes/structuralTypes.js";
|
|
3
|
+
import { type HandoffOwner } from "../agent/verification.js";
|
|
3
4
|
export interface ClipboardHandoffFingerprint {
|
|
4
5
|
readonly key: string;
|
|
5
6
|
readonly fingerprint: string;
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
interface ClipboardDraftHandoff {
|
|
9
|
+
readonly owner: HandoffOwner;
|
|
8
10
|
readonly changes: readonly ClipboardHandoffFingerprint[];
|
|
9
11
|
readonly structuralChanges: readonly ClipboardHandoffFingerprint[];
|
|
10
12
|
}
|
|
11
|
-
|
|
13
|
+
export interface ClipboardHandoffSnapshot {
|
|
14
|
+
readonly drafts: readonly ClipboardDraftHandoff[];
|
|
15
|
+
}
|
|
12
16
|
export declare function isClipboardHandoffSnapshot(value: unknown): value is ClipboardHandoffSnapshot | null;
|
|
13
|
-
|
|
14
|
-
export declare function recordClipboardHandoff(changes: readonly ChangeRecord[], structuralChanges?: readonly StructuralChange[]): void;
|
|
15
|
-
/** Returns a defensive copy for durable-session serialization. */
|
|
17
|
+
export declare function recordClipboardHandoff(changes: readonly ChangeRecord[], structuralChanges?: readonly StructuralChange[], owner?: HandoffOwner): void;
|
|
16
18
|
export declare function getClipboardHandoffSnapshot(): ClipboardHandoffSnapshot | null;
|
|
17
|
-
/** Replaces the in-memory checkpoint from a validated durable session. */
|
|
18
19
|
export declare function hydrateClipboardHandoff(value: ClipboardHandoffSnapshot | null): void;
|
|
19
20
|
export declare function clearClipboardHandoff(): void;
|
|
20
21
|
export declare function subscribeClipboardHandoff(listener: () => void): () => void;
|
|
21
|
-
/** Monotonic external-store snapshot for persistence and React consumers. */
|
|
22
22
|
export declare function getClipboardHandoffRevision(): number;
|
|
23
23
|
export declare function getLastClipboardReconciledCount(): number;
|
|
24
|
-
/**
|
|
25
|
-
* Checks a copied prompt against source-rendered output and reconciles only
|
|
26
|
-
* exact records that the browser can positively verify.
|
|
27
|
-
*/
|
|
28
24
|
export declare function reconcileClipboardHandoff(doc?: Document): Promise<number>;
|
|
29
|
-
/**
|
|
30
|
-
* Watches source-driven document refreshes and page-return signals. The
|
|
31
|
-
* trailing debounce lets an HMR commit settle before verification lifts the
|
|
32
|
-
* inspector preview for two frames.
|
|
33
|
-
*/
|
|
34
25
|
export declare function startClipboardHandoffController(doc?: Document): () => void;
|
|
26
|
+
export {};
|
|
@@ -1,212 +1,161 @@
|
|
|
1
1
|
import { changeKey } from "../changes/model.js";
|
|
2
|
-
import {
|
|
3
|
-
import {} from "../projection/structuralProjection.js";
|
|
4
|
-
import { getWorkspaceChanges } from "../changes/workspaceChanges.js";
|
|
2
|
+
import { getDraftChanges, getActiveDraftChanges, subscribeActiveDraftChanges } from "../changes/draftChanges.js";
|
|
5
3
|
import { documentRevisions, subscribeDocumentRevision } from "../tokens/resolution/cssomCollector.js";
|
|
6
|
-
import { handoffChangeFingerprint, handoffStructuralFingerprint, verifyAndReconcileHandoff, } from "../agent/verification.js";
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const MAX_FINGERPRINT_LENGTH = 1_048_576;
|
|
10
|
-
const RECONCILE_DEBOUNCE_MS = 450;
|
|
11
|
-
let checkpoint = null;
|
|
4
|
+
import { captureHandoffOwner, handoffChangeFingerprint, handoffStructuralFingerprint, verifyAndReconcileHandoff, } from "../agent/verification.js";
|
|
5
|
+
import { isDraftTarget } from "../drafts/model.js";
|
|
6
|
+
const checkpoints = new Map();
|
|
12
7
|
let revision = 0;
|
|
13
|
-
|
|
8
|
+
const reconciledCounts = new Map();
|
|
14
9
|
const listeners = new Set();
|
|
15
10
|
function notify() {
|
|
16
11
|
revision += 1;
|
|
17
12
|
for (const listener of listeners)
|
|
18
13
|
listener();
|
|
19
14
|
}
|
|
20
|
-
function cloneSnapshot(snapshot) {
|
|
21
|
-
return {
|
|
22
|
-
changes: snapshot.changes.map((entry) => ({ ...entry })),
|
|
23
|
-
structuralChanges: snapshot.structuralChanges.map((entry) => ({ ...entry })),
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
15
|
function isFingerprint(value) {
|
|
27
|
-
if (!value || typeof value !== "object"
|
|
16
|
+
if (!value || typeof value !== "object")
|
|
28
17
|
return false;
|
|
29
18
|
const entry = value;
|
|
30
|
-
return
|
|
31
|
-
&& typeof entry.
|
|
32
|
-
&& entry.key.length > 0
|
|
33
|
-
&& entry.key.length <= MAX_KEY_LENGTH
|
|
34
|
-
&& typeof entry.fingerprint === "string"
|
|
35
|
-
&& entry.fingerprint.length > 0
|
|
36
|
-
&& entry.fingerprint.length <= MAX_FINGERPRINT_LENGTH;
|
|
19
|
+
return typeof entry.key === "string" && entry.key.length > 0 && entry.key.length <= 16_384
|
|
20
|
+
&& typeof entry.fingerprint === "string" && entry.fingerprint.length > 0 && entry.fingerprint.length <= 1_048_576;
|
|
37
21
|
}
|
|
38
|
-
/** Validates an origin-local persisted clipboard checkpoint. */
|
|
39
22
|
export function isClipboardHandoffSnapshot(value) {
|
|
40
23
|
if (value === null)
|
|
41
24
|
return true;
|
|
42
|
-
if (!value || typeof value !== "object"
|
|
25
|
+
if (!value || typeof value !== "object")
|
|
43
26
|
return false;
|
|
44
|
-
const
|
|
45
|
-
if (!
|
|
46
|
-
|| !Array.isArray(candidate.changes)
|
|
47
|
-
|| !Array.isArray(candidate.structuralChanges)
|
|
48
|
-
|| candidate.changes.length + candidate.structuralChanges.length > MAX_HANDOFF_RECORDS
|
|
49
|
-
|| !candidate.changes.every(isFingerprint)
|
|
50
|
-
|| !candidate.structuralChanges.every(isFingerprint)) {
|
|
27
|
+
const snapshot = value;
|
|
28
|
+
if (!Array.isArray(snapshot.drafts) || snapshot.drafts.length > 512)
|
|
51
29
|
return false;
|
|
30
|
+
const ids = new Set();
|
|
31
|
+
for (const draft of snapshot.drafts) {
|
|
32
|
+
const owner = draft?.owner;
|
|
33
|
+
if (!owner || typeof owner.draftId !== "string" || ids.has(owner.draftId)
|
|
34
|
+
|| !Number.isSafeInteger(owner.draftRevision) || owner.draftRevision < 0 || !isDraftTarget(owner.target)
|
|
35
|
+
|| !Array.isArray(owner.frameIds) || owner.frameIds.some((id) => typeof id !== "string")
|
|
36
|
+
|| !Array.isArray(draft.changes) || !Array.isArray(draft.structuralChanges)
|
|
37
|
+
|| draft.changes.length + draft.structuralChanges.length > 10_000
|
|
38
|
+
|| !draft.changes.every(isFingerprint) || !draft.structuralChanges.every(isFingerprint)
|
|
39
|
+
|| new Set(draft.changes.map((entry) => entry.key)).size !== draft.changes.length
|
|
40
|
+
|| new Set(draft.structuralChanges.map((entry) => entry.key)).size !== draft.structuralChanges.length)
|
|
41
|
+
return false;
|
|
42
|
+
ids.add(owner.draftId);
|
|
52
43
|
}
|
|
53
|
-
|
|
54
|
-
const structuralKeys = candidate.structuralChanges.map((entry) => entry.key);
|
|
55
|
-
return new Set(changeKeys).size === changeKeys.length
|
|
56
|
-
&& new Set(structuralKeys).size === structuralKeys.length;
|
|
44
|
+
return true;
|
|
57
45
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
changes: changes.map((change) => ({
|
|
62
|
-
|
|
63
|
-
fingerprint: handoffChangeFingerprint(change),
|
|
64
|
-
})),
|
|
65
|
-
structuralChanges: structuralChanges.map((change) => ({
|
|
66
|
-
key: change.id,
|
|
67
|
-
fingerprint: handoffStructuralFingerprint(change),
|
|
68
|
-
})),
|
|
46
|
+
function fingerprintSnapshot(owner, changes, structuralChanges) {
|
|
47
|
+
return {
|
|
48
|
+
owner: structuredClone(owner),
|
|
49
|
+
changes: changes.map((change) => ({ key: changeKey(change), fingerprint: handoffChangeFingerprint(change) })),
|
|
50
|
+
structuralChanges: structuralChanges.map((change) => ({ key: change.id, fingerprint: handoffStructuralFingerprint(change) })),
|
|
69
51
|
};
|
|
70
|
-
|
|
52
|
+
}
|
|
53
|
+
export function recordClipboardHandoff(changes, structuralChanges = [], owner = captureHandoffOwner()) {
|
|
54
|
+
checkpoints.set(owner.draftId, fingerprintSnapshot(owner, changes, structuralChanges));
|
|
55
|
+
reconciledCounts.delete(owner.draftId);
|
|
71
56
|
notify();
|
|
72
57
|
}
|
|
73
|
-
/** Returns a defensive copy for durable-session serialization. */
|
|
74
58
|
export function getClipboardHandoffSnapshot() {
|
|
75
|
-
return
|
|
59
|
+
return checkpoints.size ? structuredClone({ drafts: [...checkpoints.values()] }) : null;
|
|
76
60
|
}
|
|
77
|
-
/** Replaces the in-memory checkpoint from a validated durable session. */
|
|
78
61
|
export function hydrateClipboardHandoff(value) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
62
|
+
checkpoints.clear();
|
|
63
|
+
for (const draft of value?.drafts ?? [])
|
|
64
|
+
checkpoints.set(draft.owner.draftId, structuredClone(draft));
|
|
65
|
+
reconciledCounts.clear();
|
|
83
66
|
notify();
|
|
84
67
|
}
|
|
85
68
|
export function clearClipboardHandoff() {
|
|
86
|
-
if (!
|
|
69
|
+
if (!checkpoints.size && !reconciledCounts.size)
|
|
87
70
|
return;
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
checkpoints.clear();
|
|
72
|
+
reconciledCounts.clear();
|
|
90
73
|
notify();
|
|
91
74
|
}
|
|
92
75
|
export function subscribeClipboardHandoff(listener) {
|
|
93
76
|
listeners.add(listener);
|
|
94
77
|
return () => listeners.delete(listener);
|
|
95
78
|
}
|
|
96
|
-
|
|
97
|
-
export function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
function matchingStructuralChanges(snapshot) {
|
|
108
|
-
const expected = new Map(snapshot.structuralChanges.map((entry) => [entry.key, entry.fingerprint]));
|
|
109
|
-
return getWorkspaceChanges().structuralChanges.filter((change) => expected.get(change.id) === handoffStructuralFingerprint(change));
|
|
79
|
+
export function getClipboardHandoffRevision() { return revision; }
|
|
80
|
+
export function getLastClipboardReconciledCount() { return reconciledCounts.get(getActiveDraftChanges().draftId) ?? 0; }
|
|
81
|
+
function matchingRecords(snapshot) {
|
|
82
|
+
const workspace = getDraftChanges(snapshot.owner.draftId);
|
|
83
|
+
const changes = new Map(snapshot.changes.map((entry) => [entry.key, entry.fingerprint]));
|
|
84
|
+
const structures = new Map(snapshot.structuralChanges.map((entry) => [entry.key, entry.fingerprint]));
|
|
85
|
+
return {
|
|
86
|
+
changes: workspace.changes.filter((change) => changes.get(changeKey(change)) === handoffChangeFingerprint(change)),
|
|
87
|
+
structuralChanges: workspace.structuralChanges.filter((change) => structures.get(change.id) === handoffStructuralFingerprint(change)),
|
|
88
|
+
};
|
|
110
89
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
90
|
+
function pruneCheckpoints() {
|
|
91
|
+
let changed = false;
|
|
92
|
+
for (const [id, snapshot] of checkpoints) {
|
|
93
|
+
const matching = matchingRecords(snapshot);
|
|
94
|
+
if (matching.changes.length === snapshot.changes.length && matching.structuralChanges.length === snapshot.structuralChanges.length)
|
|
95
|
+
continue;
|
|
96
|
+
changed = true;
|
|
97
|
+
if (matching.changes.length + matching.structuralChanges.length === 0)
|
|
98
|
+
checkpoints.delete(id);
|
|
99
|
+
else
|
|
100
|
+
checkpoints.set(id, fingerprintSnapshot(snapshot.owner, matching.changes, matching.structuralChanges));
|
|
120
101
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
: {
|
|
124
|
-
changes: changes.map((change) => ({
|
|
125
|
-
key: changeKey(change),
|
|
126
|
-
fingerprint: handoffChangeFingerprint(change),
|
|
127
|
-
})),
|
|
128
|
-
structuralChanges: structuralChanges.map((change) => ({
|
|
129
|
-
key: change.id,
|
|
130
|
-
fingerprint: handoffStructuralFingerprint(change),
|
|
131
|
-
})),
|
|
132
|
-
};
|
|
133
|
-
notify();
|
|
134
|
-
return true;
|
|
102
|
+
if (changed)
|
|
103
|
+
notify();
|
|
135
104
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Checks a copied prompt against source-rendered output and reconciles only
|
|
138
|
-
* exact records that the browser can positively verify.
|
|
139
|
-
*/
|
|
140
105
|
export async function reconcileClipboardHandoff(doc = document) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
106
|
+
let removed = 0;
|
|
107
|
+
for (const snapshot of [...checkpoints.values()]) {
|
|
108
|
+
const matching = matchingRecords(snapshot);
|
|
109
|
+
if (matching.changes.length + matching.structuralChanges.length === 0)
|
|
110
|
+
continue;
|
|
111
|
+
const count = await verifyAndReconcileHandoff({ owner: snapshot.owner, ...matching }, doc);
|
|
112
|
+
if (count > 0)
|
|
113
|
+
reconciledCounts.set(snapshot.owner.draftId, count);
|
|
114
|
+
removed += count;
|
|
149
115
|
}
|
|
150
|
-
|
|
151
|
-
pruneCheckpoint();
|
|
116
|
+
pruneCheckpoints();
|
|
152
117
|
if (removed > 0) {
|
|
153
|
-
lastReconciledCount = removed;
|
|
154
118
|
notify();
|
|
155
119
|
}
|
|
156
120
|
return removed;
|
|
157
121
|
}
|
|
158
|
-
/**
|
|
159
|
-
* Watches source-driven document refreshes and page-return signals. The
|
|
160
|
-
* trailing debounce lets an HMR commit settle before verification lifts the
|
|
161
|
-
* inspector preview for two frames.
|
|
162
|
-
*/
|
|
163
122
|
export function startClipboardHandoffController(doc = document) {
|
|
164
|
-
const
|
|
123
|
+
const view = doc.defaultView;
|
|
165
124
|
let timer = null;
|
|
166
125
|
let checking = false;
|
|
167
126
|
let stopped = false;
|
|
168
127
|
const schedule = () => {
|
|
169
|
-
if (stopped || checking || !
|
|
128
|
+
if (stopped || checking || !checkpoints.size)
|
|
170
129
|
return;
|
|
171
130
|
if (timer !== null)
|
|
172
131
|
clearTimeout(timer);
|
|
173
132
|
timer = setTimeout(() => {
|
|
174
133
|
timer = null;
|
|
175
|
-
if (stopped || checking || !
|
|
134
|
+
if (stopped || checking || !checkpoints.size)
|
|
176
135
|
return;
|
|
177
136
|
checking = true;
|
|
178
|
-
void reconcileClipboardHandoff(doc)
|
|
179
|
-
.catch(() => undefined)
|
|
180
|
-
.finally(() => {
|
|
181
|
-
// Keep the guard through the MutationObserver microtask generated by
|
|
182
|
-
// restoring managed projections, preventing a verification loop.
|
|
137
|
+
void reconcileClipboardHandoff(doc).catch(() => undefined).finally(() => {
|
|
183
138
|
setTimeout(() => { checking = false; }, 0);
|
|
184
139
|
});
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
const onCanonicalChange = () => {
|
|
188
|
-
pruneCheckpoint();
|
|
189
|
-
};
|
|
190
|
-
const onVisibilityChange = () => {
|
|
191
|
-
if (doc.visibilityState === "visible")
|
|
192
|
-
schedule();
|
|
140
|
+
}, 450);
|
|
193
141
|
};
|
|
194
|
-
|
|
142
|
+
const visible = () => { if (doc.visibilityState === "visible")
|
|
143
|
+
schedule(); };
|
|
195
144
|
documentRevisions(doc);
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
doc.addEventListener("visibilitychange",
|
|
145
|
+
const stopRevision = subscribeDocumentRevision(doc, schedule);
|
|
146
|
+
const stopChanges = subscribeActiveDraftChanges(pruneCheckpoints);
|
|
147
|
+
view?.addEventListener("focus", schedule);
|
|
148
|
+
view?.addEventListener("pageshow", schedule);
|
|
149
|
+
doc.addEventListener("visibilitychange", visible);
|
|
201
150
|
schedule();
|
|
202
151
|
return () => {
|
|
203
152
|
stopped = true;
|
|
204
153
|
if (timer !== null)
|
|
205
154
|
clearTimeout(timer);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
doc.removeEventListener("visibilitychange",
|
|
155
|
+
stopRevision();
|
|
156
|
+
stopChanges();
|
|
157
|
+
view?.removeEventListener("focus", schedule);
|
|
158
|
+
view?.removeEventListener("pageshow", schedule);
|
|
159
|
+
doc.removeEventListener("visibilitychange", visible);
|
|
211
160
|
};
|
|
212
161
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ChangeRecord } from "../changes/changesLog.js";
|
|
2
2
|
import type { StructuralChange } from "../projection/structuralProjection.js";
|
|
3
3
|
import type { AgentSketchMetadata } from "../agent/protocol.js";
|
|
4
|
+
import { type ElementComment } from "../comments/store.js";
|
|
4
5
|
export interface FrameworkHints {
|
|
5
6
|
framework?: string;
|
|
6
7
|
stylingSystem?: string;
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
+
/** Counts the final edit intents exported by the prompt, preserving gesture history for undo. */
|
|
10
|
+
export declare function countPromptChanges(changes: readonly ChangeRecord[], structuralChanges?: readonly StructuralChange[], sketchCount?: number): number;
|
|
11
|
+
export declare function generatePrompt(changes: ChangeRecord[], frameworkHints?: FrameworkHints, structuralChanges?: readonly StructuralChange[], customInstructions?: string, sketches?: readonly AgentSketchMetadata[], comments?: readonly ElementComment[]): string;
|
|
@@ -5,6 +5,8 @@ import { formatComponentPropBaseline, formatComponentPropValue, } from "../compo
|
|
|
5
5
|
import { boundRuntimeEvidence, isRuntimeGeneratedSource, normalizeRuntimeTag, normalizeRuntimeText, } from "../runtime/staticHtmlRuntimeIdentity.js";
|
|
6
6
|
import { getSourceCoordinatePolicy } from "../runtime/runtimeConfig.js";
|
|
7
7
|
import { DEFAULT_CUSTOM_INSTRUCTIONS } from "./promptSettings.js";
|
|
8
|
+
import { getCommentsForTarget } from "../comments/store.js";
|
|
9
|
+
import { documentTarget } from "../drafts/model.js";
|
|
8
10
|
const EMPTY_SENTINEL = "<!-- No changes to export -->\n\nThe changes log is empty. Make a change in the Nudge UI inspector first.";
|
|
9
11
|
function renderPrompt(sections, customInstructions) {
|
|
10
12
|
const lines = [
|
|
@@ -244,17 +246,6 @@ function structuralChangeLine(change) {
|
|
|
244
246
|
? `- Move ${target} before ${renderedInstanceDescription(before)} in ${destinationParent}.`
|
|
245
247
|
: `- Move ${target} to the end of ${destinationParent}.`;
|
|
246
248
|
}
|
|
247
|
-
function sketchLines(sketches) {
|
|
248
|
-
const titles = [...new Set(sketches.map((sketch) => sketch.capture.title || "(untitled)"))];
|
|
249
|
-
return [
|
|
250
|
-
...titles.map((title) => `- Page title: ${promptText(title)}`),
|
|
251
|
-
...sketches.flatMap((sketch) => [
|
|
252
|
-
...(sketch.description ? [`- Description: ${promptText(sketch.description)}`] : []),
|
|
253
|
-
...(sketch.annotations ?? []).map((annotation) => `- Annotation ${annotation.number}: ${promptText(annotation.description)}`),
|
|
254
|
-
]),
|
|
255
|
-
"- Interpret the attached frozen screenshot, blue strokes, and numbered annotations as visual feedback. Do not derive selectors, source coordinates, or semantics from the strokes. Use each annotation number to match the note to its numbered dot.",
|
|
256
|
-
];
|
|
257
|
-
}
|
|
258
249
|
function renderedInstanceKey(ref) {
|
|
259
250
|
const { sourceSite, locator } = ref;
|
|
260
251
|
return JSON.stringify([
|
|
@@ -315,10 +306,14 @@ function canonicalizeStructuralChanges(changes) {
|
|
|
315
306
|
}
|
|
316
307
|
return canonical;
|
|
317
308
|
}
|
|
318
|
-
|
|
309
|
+
/** Counts the final edit intents exported by the prompt, preserving gesture history for undo. */
|
|
310
|
+
export function countPromptChanges(changes, structuralChanges = [], sketchCount = 0) {
|
|
311
|
+
return canonicalizeChanges([...changes]).length + canonicalizeStructuralChanges(structuralChanges).length + sketchCount;
|
|
312
|
+
}
|
|
313
|
+
export function generatePrompt(changes, frameworkHints, structuralChanges = [], customInstructions = DEFAULT_CUSTOM_INSTRUCTIONS, sketches = [], comments = getCommentsForTarget(documentTarget(window.location.href))) {
|
|
319
314
|
const deduplicated = canonicalizeChanges(changes);
|
|
320
315
|
const structuralIntent = canonicalizeStructuralChanges(structuralChanges);
|
|
321
|
-
if (deduplicated.length === 0 && structuralIntent.length === 0 && sketches.length === 0)
|
|
316
|
+
if (deduplicated.length === 0 && structuralIntent.length === 0 && sketches.length === 0 && comments.length === 0)
|
|
322
317
|
return EMPTY_SENTINEL;
|
|
323
318
|
const tokenChanges = deduplicated.filter(isTokenChange);
|
|
324
319
|
const componentChanges = deduplicated.filter(isComponentChange);
|
|
@@ -398,10 +393,16 @@ export function generatePrompt(changes, frameworkHints, structuralChanges = [],
|
|
|
398
393
|
lines: structuralIntent.map(structuralChangeLine),
|
|
399
394
|
});
|
|
400
395
|
}
|
|
401
|
-
if (
|
|
396
|
+
if (comments.length > 0) {
|
|
402
397
|
sections.push({
|
|
403
|
-
heading: "
|
|
404
|
-
lines:
|
|
398
|
+
heading: "Element comments",
|
|
399
|
+
lines: comments.flatMap((comment) => [
|
|
400
|
+
`### <${comment.tag}> (${comment.target.sourceSite.src || "source unknown"})`,
|
|
401
|
+
`- Page: ${promptText(comment.route)}`,
|
|
402
|
+
`- Target: ${renderedInstanceDescription(comment.target)}`,
|
|
403
|
+
`- Note: ${promptText(comment.note)}`,
|
|
404
|
+
"",
|
|
405
|
+
]),
|
|
405
406
|
});
|
|
406
407
|
}
|
|
407
408
|
return renderPrompt(sections, customInstructions);
|
|
@@ -24,7 +24,7 @@ export interface NudgeUiRuntimeCapabilities {
|
|
|
24
24
|
readonly canvas: boolean;
|
|
25
25
|
/** Whether framework component inspection and prop overrides are available. */
|
|
26
26
|
readonly componentSemantics: boolean;
|
|
27
|
-
/** Whether the
|
|
27
|
+
/** Whether the DOM navigator overlay is available. */
|
|
28
28
|
readonly domNavigation?: boolean;
|
|
29
29
|
/**
|
|
30
30
|
* Source-coordinate precision policy for prompts; every source site is
|
|
@@ -39,6 +39,11 @@ export interface NudgeUiRuntimeCapabilities {
|
|
|
39
39
|
*/
|
|
40
40
|
readonly scopingSelectorPattern?: string;
|
|
41
41
|
}
|
|
42
|
+
/** Product features that a host can selectively expose in its inspector UI. */
|
|
43
|
+
export interface NudgeUiRuntimeFeatures {
|
|
44
|
+
/** Whether Canvas can create filesystem-backed HTML iterations. */
|
|
45
|
+
readonly canvasIterations?: boolean;
|
|
46
|
+
}
|
|
42
47
|
/**
|
|
43
48
|
* Browser knowledge supplied by a host Adapter before the inspector mounts.
|
|
44
49
|
*
|
|
@@ -53,6 +58,8 @@ export interface NudgeUiRuntimeConfig {
|
|
|
53
58
|
readonly framework: NudgeUiRuntimeFramework;
|
|
54
59
|
readonly stylingSystem: string;
|
|
55
60
|
readonly capabilities: NudgeUiRuntimeCapabilities;
|
|
61
|
+
/** Product-level feature switches; omitted features remain enabled. */
|
|
62
|
+
readonly features?: NudgeUiRuntimeFeatures;
|
|
56
63
|
readonly tokenCatalog: readonly TokenDefinition[];
|
|
57
64
|
readonly tokens: readonly TokenEntry[];
|
|
58
65
|
readonly tokenDiagnostics: readonly TokenCatalogDiagnostic[];
|
|
@@ -91,6 +98,8 @@ export declare function normalizeNudgeUiRuntimeConfig(input: unknown): NudgeUiRu
|
|
|
91
98
|
export declare function configureNudgeUiRuntime(config: NudgeUiRuntimeConfig): void;
|
|
92
99
|
/** Returns the immutable runtime snapshot used by shared inspector Modules. */
|
|
93
100
|
export declare function getNudgeUiRuntimeConfig(): NudgeUiRuntimeConfig;
|
|
101
|
+
/** Returns whether a product feature is enabled; omitted flags default to enabled. */
|
|
102
|
+
export declare function isNudgeUiFeatureEnabled(features: NudgeUiRuntimeFeatures | undefined, feature: keyof NudgeUiRuntimeFeatures): boolean;
|
|
94
103
|
/**
|
|
95
104
|
* Whether this document runs the explicit public landing demo (ADR-0014 and
|
|
96
105
|
* ADR-0016). The plugin emits `demo: true` for the flagged demo route and for
|
|
@@ -98,6 +98,15 @@ function optionalBoolean(input, field) {
|
|
|
98
98
|
}
|
|
99
99
|
return value;
|
|
100
100
|
}
|
|
101
|
+
function optionalFeatureBoolean(input, field) {
|
|
102
|
+
const value = input[field];
|
|
103
|
+
if (value === undefined)
|
|
104
|
+
return undefined;
|
|
105
|
+
if (typeof value !== "boolean") {
|
|
106
|
+
throw new TypeError(`Nudge UI runtime feature "${field}" must be a boolean; received ${JSON.stringify(value)}.`);
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
101
110
|
function optionalNonEmptyString(input, field) {
|
|
102
111
|
const value = input[field];
|
|
103
112
|
if (value === undefined)
|
|
@@ -142,6 +151,15 @@ function normalizeCapabilities(input) {
|
|
|
142
151
|
scopingSelectorPattern: optionalNonEmptyString(input, "scopingSelectorPattern"),
|
|
143
152
|
};
|
|
144
153
|
}
|
|
154
|
+
function normalizeFeatures(input) {
|
|
155
|
+
if (input === undefined)
|
|
156
|
+
return undefined;
|
|
157
|
+
if (!isPlainRecord(input)) {
|
|
158
|
+
throw new TypeError('Nudge UI runtime configuration field "features" must be an object.');
|
|
159
|
+
}
|
|
160
|
+
const canvasIterations = optionalFeatureBoolean(input, "canvasIterations");
|
|
161
|
+
return canvasIterations === undefined ? {} : { canvasIterations };
|
|
162
|
+
}
|
|
145
163
|
function optionalDemoFlag(input) {
|
|
146
164
|
const value = input.demo;
|
|
147
165
|
if (value === undefined || value === false)
|
|
@@ -172,12 +190,14 @@ export function normalizeNudgeUiRuntimeConfig(input) {
|
|
|
172
190
|
const demo = optionalDemoFlag(input);
|
|
173
191
|
const demoPages = optionalStringArray(input, "demoPages");
|
|
174
192
|
const demoCardLabels = optionalStringArray(input, "demoCardLabels");
|
|
193
|
+
const features = normalizeFeatures(input.features);
|
|
175
194
|
const normalized = cloneAndFreeze({
|
|
176
195
|
projectId: requireString(input, "projectId"),
|
|
177
196
|
host: requireEnum(input, "host", RUNTIME_HOSTS),
|
|
178
197
|
framework: requireEnum(input, "framework", RUNTIME_FRAMEWORKS),
|
|
179
198
|
stylingSystem: optionalString(input, "stylingSystem"),
|
|
180
199
|
capabilities: normalizeCapabilities(input.capabilities),
|
|
200
|
+
...(features === undefined ? {} : { features }),
|
|
181
201
|
tokenCatalog: optionalArray(input, "tokenCatalog"),
|
|
182
202
|
tokens: optionalArray(input, "tokens"),
|
|
183
203
|
tokenDiagnostics: optionalArray(input, "tokenDiagnostics"),
|
|
@@ -217,6 +237,10 @@ export function configureNudgeUiRuntime(config) {
|
|
|
217
237
|
export function getNudgeUiRuntimeConfig() {
|
|
218
238
|
return activeRuntimeConfig;
|
|
219
239
|
}
|
|
240
|
+
/** Returns whether a product feature is enabled; omitted flags default to enabled. */
|
|
241
|
+
export function isNudgeUiFeatureEnabled(features, feature) {
|
|
242
|
+
return features?.[feature] !== false;
|
|
243
|
+
}
|
|
220
244
|
/**
|
|
221
245
|
* Whether this document runs the explicit public landing demo (ADR-0014 and
|
|
222
246
|
* ADR-0016). The plugin emits `demo: true` for the flagged demo route and for
|
|
@@ -3,5 +3,11 @@ export interface DescendantNode {
|
|
|
3
3
|
element: HTMLElement;
|
|
4
4
|
depth: number;
|
|
5
5
|
}
|
|
6
|
+
export interface NavigationNode {
|
|
7
|
+
element: HTMLElement;
|
|
8
|
+
direction: "up" | "down";
|
|
9
|
+
depth: number;
|
|
10
|
+
}
|
|
6
11
|
/** Returns the nearest tracked descendants, breadth-first and document-ordered. */
|
|
7
12
|
export declare function computeDescendants(el: HTMLElement, maxDepth?: number, maxNodes?: number): DescendantNode[];
|
|
13
|
+
export declare function computeNavigationNodes(el: HTMLElement, hierarchy?: HTMLElement[]): NavigationNode[];
|
|
@@ -40,3 +40,11 @@ export function computeDescendants(el, maxDepth = 2, maxNodes = 2) {
|
|
|
40
40
|
.sort((left, right) => left.depth - right.depth || left.order - right.order);
|
|
41
41
|
return descendants.slice(0, maxNodes).map(({ element, depth }) => ({ element, depth }));
|
|
42
42
|
}
|
|
43
|
+
export function computeNavigationNodes(el, hierarchy = computeHierarchy(el)) {
|
|
44
|
+
const children = computeDescendants(el, 2, 2);
|
|
45
|
+
const parents = hierarchy.slice(1, (children.length > 0 ? 2 : 4) + 1);
|
|
46
|
+
return [
|
|
47
|
+
...parents.map((element, index) => ({ element, direction: "up", depth: index + 1 })),
|
|
48
|
+
...children.map(({ element, depth }) => ({ element, direction: "down", depth })),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
@@ -9,4 +9,4 @@ export interface SettingsDialogProps extends McpConnectionContentProps {
|
|
|
9
9
|
readonly onOpenChange: (open: boolean) => void;
|
|
10
10
|
}
|
|
11
11
|
/** Provides one settings surface for prompt, MCP, and token configuration. */
|
|
12
|
-
export declare function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onDisconnect, onCheckAgain, }: SettingsDialogProps): ReactElement;
|
|
12
|
+
export declare function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onTakeOver, onDisconnect, onCheckAgain, }: SettingsDialogProps): ReactElement;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { Dialog } from "@base-ui/react/dialog";
|
|
4
|
-
import { IconColorSwatch, IconPlugConnected, IconSettings, IconX, } from "@tabler/icons-react";
|
|
4
|
+
import { IconColorSwatch, IconPlugConnected, IconSettings, IconX, IconTrash, } from "@tabler/icons-react";
|
|
5
5
|
import { useBrowserCssInspection } from "../inspection/useBrowserCssInspection.js";
|
|
6
6
|
import { McpConnectionContent, } from "../agent/McpConnectionDialog.js";
|
|
7
7
|
import { PromptSettingsFields, } from "../prompt/PromptSettingsDialog.js";
|
|
@@ -10,10 +10,17 @@ import { useSelectedElement } from "../selection/selectionStore.js";
|
|
|
10
10
|
import { useFocusedCardId, useSelectedCardId } from "../canvas/canvasStore.js";
|
|
11
11
|
import { getRegisteredFrames } from "../canvas/projection.js";
|
|
12
12
|
import { getBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
|
|
13
|
+
import { portalContainer } from "../ui/portalContainer.js";
|
|
14
|
+
import { clearRestoreCount, clearSession } from "../canvas/sessionStore.js";
|
|
15
|
+
import { hasWriteLease } from "../canvas/workspaceLease.js";
|
|
16
|
+
import { cancelSketchInteraction } from "../sketch/interaction.js";
|
|
17
|
+
import { closeSketchNote } from "../sketch/sketchNote.js";
|
|
18
|
+
import { clearSketchClipboardHandoff } from "../sketch/handoff.js";
|
|
19
|
+
import { clearSketchesForProject } from "../sketch/store.js";
|
|
13
20
|
const SETTINGS_SECTIONS = [
|
|
14
21
|
{
|
|
15
22
|
id: "instructions",
|
|
16
|
-
label: "
|
|
23
|
+
label: "Instructions",
|
|
17
24
|
icon: _jsx(IconSettings, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }),
|
|
18
25
|
},
|
|
19
26
|
{
|
|
@@ -27,13 +34,8 @@ const SETTINGS_SECTIONS = [
|
|
|
27
34
|
icon: _jsx(IconColorSwatch, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }),
|
|
28
35
|
},
|
|
29
36
|
];
|
|
30
|
-
function portalContainer() {
|
|
31
|
-
return typeof document !== "undefined"
|
|
32
|
-
? document.getElementById("nudge-ui-root")?.shadowRoot ?? document.body
|
|
33
|
-
: null;
|
|
34
|
-
}
|
|
35
37
|
/** Provides one settings surface for prompt, MCP, and token configuration. */
|
|
36
|
-
export function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onDisconnect, onCheckAgain, }) {
|
|
38
|
+
export function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onTakeOver, onDisconnect, onCheckAgain, }) {
|
|
37
39
|
const [activeSection, setActiveSection] = useState(initialSection);
|
|
38
40
|
const closeRef = useRef(null);
|
|
39
41
|
const instructionsRef = useRef(null);
|
|
@@ -52,5 +54,16 @@ export function SettingsDialog({ open, initialSection, value, onValueChange, onO
|
|
|
52
54
|
if (open)
|
|
53
55
|
setActiveSection(initialSection);
|
|
54
56
|
}, [initialSection, open]);
|
|
55
|
-
|
|
57
|
+
function resetCanvas() {
|
|
58
|
+
if (!hasWriteLease())
|
|
59
|
+
return;
|
|
60
|
+
cancelSketchInteraction();
|
|
61
|
+
closeSketchNote();
|
|
62
|
+
clearSketchClipboardHandoff();
|
|
63
|
+
clearSession();
|
|
64
|
+
clearRestoreCount();
|
|
65
|
+
void clearSketchesForProject(projectId).catch(() => undefined);
|
|
66
|
+
onOpenChange(false);
|
|
67
|
+
}
|
|
68
|
+
return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { className: "settings__backdrop", "data-test": "settings-backdrop" }), _jsxs(Dialog.Popup, { className: "settings__popup", "data-test": "settings-dialog", initialFocus: closeRef, children: [_jsx(Dialog.Title, { className: "settings__visually-hidden", children: "Settings" }), _jsx(Dialog.Description, { className: "settings__visually-hidden", children: "Configure how Nudge connects to your coding agent and uses your design system." }), _jsx(Dialog.Close, { ref: closeRef, className: "icon-button icon-button--quiet settings__close", "aria-label": "Close settings", "data-test": "settings-close", type: "button", children: _jsx(IconX, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }) }), _jsxs("div", { className: "settings__body", children: [_jsxs("nav", { className: "settings__nav", "aria-label": "Settings sections", children: [_jsx("span", { className: "settings__nav-label", children: "Settings" }), _jsx("div", { className: "settings__nav-items", children: SETTINGS_SECTIONS.map((section) => (_jsxs("button", { className: `settings__nav-item${activeSection === section.id ? " settings__nav-item--active" : ""}`, "data-test": `settings-nav-${section.id}`, type: "button", "aria-current": activeSection === section.id ? "page" : undefined, onClick: () => setActiveSection(section.id), children: [section.icon, _jsx("span", { className: "settings__nav-item-label", children: section.label })] }, section.id))) }), _jsxs("button", { className: "settings__nav-item settings__reset-canvas", "data-test": "settings-reset-canvas", type: "button", onClick: resetCanvas, children: [_jsx(IconTrash, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" }), _jsx("span", { className: "settings__nav-item-label", children: "Reset canvas" })] })] }), _jsxs("main", { className: "settings__content", "data-test": "settings-content", children: [activeSection === "instructions" ? (_jsxs("section", { className: "settings__section", "data-test": "prompt-settings-dialog", "data-settings-section": "instructions", children: [_jsx("h2", { className: "settings__section-title", children: "Custom instructions" }), _jsx("p", { className: "settings__section-description", children: "These instructions are added to the end of every copied prompt." }), _jsx(PromptSettingsFields, { value: value, onChange: onValueChange, instructionsRef: instructionsRef })] })) : null, activeSection === "mcp" ? (_jsxs("section", { className: "settings__section", "data-test": "settings-section-mcp", children: [_jsx("h2", { className: "settings__section-title", children: activeDefinition.label }), _jsx("div", { "data-test": "mcp-connection-dialog", children: _jsx(McpConnectionContent, { projectId: projectId, origin: origin, snapshot: snapshot, onConnect: onConnect, onTakeOver: onTakeOver, onDisconnect: onDisconnect, onCheckAgain: onCheckAgain }) })] })) : null, activeSection === "tokens" ? (_jsxs("section", { className: "settings__section", "data-test": "settings-section-tokens", children: [_jsx("h2", { className: "settings__section-title", children: activeDefinition.label }), _jsx("p", { className: "settings__section-description", children: "Inspect and edit the design tokens available in this page." }), _jsx(TokensPanel, { rows: tokenRows })] })) : null] })] })] })] }) }));
|
|
56
69
|
}
|