nudge-ui 0.2.2 → 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 -61
- 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 +38 -32
- package/dist/hosts/astro/integration.js +5 -1
- package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
- package/dist/hosts/astro/markdownIdentity.js +81 -0
- package/dist/hosts/next/manifest.js +1 -1
- package/dist/hosts/next/sidecar.js +5 -1
- package/dist/hosts/static/manifest.js +1 -1
- package/dist/hosts/static/server.js +3 -0
- package/dist/hosts/vite/vite.d.ts +3 -0
- package/dist/hosts/vite/vite.js +24 -4
- package/dist/inspector/agent/McpConnectionDialog.js +13 -19
- package/dist/inspector/agent/activity.d.ts +18 -0
- package/dist/inspector/agent/activity.js +70 -0
- package/dist/inspector/agent/client.d.ts +2 -0
- package/dist/inspector/agent/client.js +29 -1
- package/dist/inspector/agent/connectionStatus.js +1 -1
- package/dist/inspector/agent/httpTransport.js +9 -2
- package/dist/inspector/agent/protocol.d.ts +6 -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 +152 -16
- 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 +225 -57
- 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 +21 -4
- package/dist/inspector/canvas/frameProtocol.js +3 -3
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -1
- package/dist/inspector/canvas/keyboardShortcuts.js +4 -4
- package/dist/inspector/canvas/layout.d.ts +52 -0
- package/dist/inspector/canvas/layout.js +106 -0
- package/dist/inspector/canvas/projection.d.ts +15 -5
- package/dist/inspector/canvas/projection.js +69 -13
- package/dist/inspector/canvas/rendererBootstrap.js +32 -4
- package/dist/inspector/canvas/rendererElementSelector.js +47 -6
- 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 +1 -1
- 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 +20 -27
- 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.js +21 -15
- package/dist/inspector/overlay/interactionStyles.js +3 -1
- package/dist/inspector/overlay/spacingGestures.d.ts +13 -1
- package/dist/inspector/overlay/spacingGestures.js +82 -5
- 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 +9 -0
- package/dist/inspector/runtime/runtimeConfig.js +24 -0
- package/dist/inspector/settings/SettingsDialog.js +21 -8
- 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 +46 -26
- package/dist/inspector/shell/CopyPromptButton.js +138 -89
- package/dist/inspector/shell/EmptyState.js +9 -8
- package/dist/inspector/shell/InspectorShell.js +13 -21
- 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/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 +7 -5
- 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 +2 -2
- package/dist/inspector/styleEditors/nudgeValue.js +36 -8
- package/dist/inspector/tokens/TokenField.d.ts +7 -0
- package/dist/inspector/tokens/TokenField.js +29 -21
- package/dist/inspector/tokens/TokenLabel.js +5 -14
- 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/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 +0 -2
- package/dist/inspector/ui/dragCursor.js +2 -10
- package/dist/inspector/ui/portalContainer.d.ts +2 -0
- package/dist/inspector/ui/portalContainer.js +6 -0
- package/dist/inspector/ui/styles.js +29 -21
- 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 +558 -311
- 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.js +3 -1
- package/dist/transport/routes.d.ts +2 -0
- package/dist/transport/routes.js +2 -0
- package/package.json +6 -2
- package/dist/inspector/changes/workspaceChanges.d.ts +0 -47
- package/dist/inspector/changes/workspaceChanges.js +0 -265
|
@@ -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,11 +34,6 @@ 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
38
|
export function SettingsDialog({ open, initialSection, value, onValueChange, onOpenChange, projectId, origin, snapshot, onConnect, onTakeOver, onDisconnect, onCheckAgain, }) {
|
|
37
39
|
const [activeSection, setActiveSection] = useState(initialSection);
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { type AgentClientSnapshot } from "../agent/client.js";
|
|
3
|
+
import type { SettingsSection } from "../settings/SettingsDialog.js";
|
|
4
|
+
export interface AgentStatusButtonProps {
|
|
5
|
+
onOpenSettings: (section: SettingsSection) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function AgentStatusButton({ onOpenSettings }: AgentStatusButtonProps): ReactElement;
|
|
8
|
+
/** Renders the connection snapshot provided by the project client. */
|
|
9
|
+
export declare function AgentStatusButtonView({ agent, onOpenSettings }: AgentStatusButtonProps & {
|
|
10
|
+
agent: AgentClientSnapshot;
|
|
11
|
+
}): ReactElement;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { IconRobot } from "@tabler/icons-react";
|
|
3
|
+
import { useAgentClient } from "../agent/client.js";
|
|
4
|
+
import { getAgentConnectionStatus } from "../agent/connectionStatus.js";
|
|
5
|
+
import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
|
|
6
|
+
import { Button } from "../ui/Button.js";
|
|
7
|
+
import { Tooltip } from "../ui/Tooltip.js";
|
|
8
|
+
/** Renders a known MCP client logo, or a generic robot avatar. */
|
|
9
|
+
function ConnectedAgentAvatar({ name }) {
|
|
10
|
+
const harness = name?.toLowerCase().match(/(?:^|[^a-z])(codex|claude|cursor)(?:$|[^a-z])/)?.[1];
|
|
11
|
+
return (_jsx("span", { className: "agent-status-button__avatars", role: "img", "aria-label": harness === "codex" ? "Codex" : harness === "claude" ? "Claude" : harness === "cursor" ? "Cursor" : "Agent", children: harness === "codex" ? _jsx(CodexAvatar, {})
|
|
12
|
+
: harness === "claude" ? _jsx(ClaudeAvatar, {})
|
|
13
|
+
: harness === "cursor" ? _jsx(CursorAvatar, {})
|
|
14
|
+
: _jsx("span", { className: "agent-status-button__avatar", children: _jsx(IconRobot, { size: 24, color: "#000", "aria-hidden": "true" }) }) }));
|
|
15
|
+
}
|
|
16
|
+
function CodexAvatar() {
|
|
17
|
+
return (_jsx("span", { className: "agent-status-button__avatar", children: _jsx("svg", { viewBox: "0 0 24 24", width: "24", height: "24", fill: "none", children: _jsx("path", { fill: "#000", d: "M20.686 10.184a4.92 4.92 0 0 0-.435-4.092 5.1 5.1 0 0 0-2.35-2.093 5.17 5.17 0 0 0-3.15-.324A5.13 5.13 0 0 0 12.53 2.25a5.2 5.2 0 0 0-2.648-.14 5.15 5.15 0 0 0-2.364 1.185 5.05 5.05 0 0 0-1.447 2.19 5.1 5.1 0 0 0-1.953.85A5 5 0 0 0 2.693 7.9a4.98 4.98 0 0 0 .627 5.914 4.92 4.92 0 0 0 .432 4.092A5.1 5.1 0 0 0 6.104 20c.99.435 2.093.548 3.151.324.478.53 1.065.955 1.722 1.243a5.1 5.1 0 0 0 2.087.433 5.16 5.16 0 0 0 3.018-.968 5.05 5.05 0 0 0 1.858-2.537 5.1 5.1 0 0 0 1.953-.849 5 5 0 0 0 1.425-1.568 4.99 4.99 0 0 0-.632-5.894m-7.622 10.507a3.8 3.8 0 0 1-2.43-.867l.12-.067 4.037-2.299a.67.67 0 0 0 .332-.568v-5.614l1.706.974a.06.06 0 0 1 .032.043v4.653a3.73 3.73 0 0 1-1.114 2.646 3.83 3.83 0 0 1-2.683 1.099m-8.161-3.438a3.68 3.68 0 0 1-.452-2.511l.12.07 4.041 2.3a.66.66 0 0 0 .66 0l4.935-2.808v1.944a.07.07 0 0 1-.027.051l-4.09 2.326a3.85 3.85 0 0 1-2.88.373 3.8 3.8 0 0 1-2.307-1.745M3.84 8.58a3.78 3.78 0 0 1 1.998-1.644v4.73a.63.63 0 0 0 .328.564l4.912 2.796-1.707.973a.07.07 0 0 1-.06 0l-4.08-2.322a3.76 3.76 0 0 1-1.758-2.267 3.7 3.7 0 0 1 .367-2.83m14.02 3.213L12.934 8.97 14.635 8a.07.07 0 0 1 .06 0l4.08 2.326a3.77 3.77 0 0 1 1.467 1.508 3.7 3.7 0 0 1-.338 3.999 3.8 3.8 0 0 1-1.7 1.246v-4.73a.65.65 0 0 0-.343-.556m1.7-2.52-.12-.07-4.034-2.319a.66.66 0 0 0-.663 0L9.811 9.691V7.748a.05.05 0 0 1 .024-.051l4.08-2.323a3.84 3.84 0 0 1 4.073.174 3.76 3.76 0 0 0 1.332 1.63c.279.657.361 1.378.24 2.08zM8.88 12.72l-1.707-.97a.07.07 0 0 1-.032-.047v-4.64c.001-.712.207-1.409.595-2.01s.94-1.079 1.594-1.38a3.85 3.85 0 0 1 4.042.512l-.12.067-4.036 2.298a.67.67 0 0 0-.332.568zm.927-1.971 2.198-1.25 2.202 1.25v2.5l-2.194 1.25-2.202-1.25z" }) }) }));
|
|
18
|
+
}
|
|
19
|
+
function ClaudeAvatar() {
|
|
20
|
+
return (_jsx("span", { className: "agent-status-button__avatar", children: _jsx("svg", { viewBox: "0 0 24 24", width: "24", height: "24", fill: "none", children: _jsx("path", { fill: "#e8704e", d: "m5.924 15.296 3.934-2.206.066-.192-.066-.106h-.192l-.658-.04-2.248-.061-1.95-.081-1.888-.101-.476-.102L2 11.82l.046-.293.4-.268.572.05 1.266.086 1.898.132 1.377.08 2.041.213h.324l.046-.131-.112-.081-.086-.081-1.964-1.33L5.68 8.79l-1.114-.81-.602-.41-.304-.384-.132-.84.547-.602.734.05.188.051.744.572 1.59 1.23 2.076 1.527.304.253.121-.086.015-.06-.137-.228-1.129-2.04L7.377 4.94l-.536-.86-.142-.517a2.5 2.5 0 0 1-.086-.607l.623-.845L7.58 2l.83.111.35.304.516 1.179.835 1.857 1.297 2.524.38.749.202.693.076.213h.131v-.122l.107-1.421.197-1.746.193-2.246.065-.633.314-.759.623-.41.486.233.4.572-.055.37-.238 1.542-.466 2.419-.304 1.619h.177l.203-.203.82-1.087 1.377-1.72.608-.684.709-.753.455-.36h.861l.633.941-.284.972-.886 1.123-.734.951-1.053 1.417-.658 1.133.06.091.158-.015 2.38-.506 1.285-.233 1.535-.263.693.324.076.329-.273.673-1.64.405-1.925.384-2.866.678-.035.025.04.051 1.292.121.551.03h1.352l2.517.188.658.435.395.531-.066.405-1.012.516-1.368-.324-3.19-.759-1.093-.273h-.152v.091l.911.89 1.671 1.508 2.091 1.943.107.48-.269.38-.283-.04-1.838-1.381-.709-.623-1.605-1.35h-.106v.141l.37.542 1.954 2.934.1.9-.14.294-.507.177-.557-.101-1.144-1.604-1.18-1.806-.952-1.62-.117.067-.562 6.046-.263.308-.607.233-.507-.384-.268-.623.268-1.23.324-1.603.264-1.275.238-1.584.141-.526-.01-.035-.116.015-1.195 1.64-1.818 2.453-1.438 1.538-.344.137-.598-.309.056-.551.334-.491 1.99-2.53 1.2-1.568.775-.906-.005-.132h-.046l-5.286 3.43-.942.122-.405-.38.05-.622.193-.202 1.59-1.093z" }) }) }));
|
|
21
|
+
}
|
|
22
|
+
function CursorAvatar() {
|
|
23
|
+
return (_jsx("span", { className: "agent-status-button__avatar", children: _jsx("svg", { viewBox: "0 0 24 24", width: "24", height: "24", fill: "none", children: _jsx("path", { fill: "#000", d: "M11.576 2.11 3.359 6.731a.7.7 0 0 0-.262.256.7.7 0 0 0-.097.349v9.323c0 .25.138.48.359.604l8.214 4.625a.87.87 0 0 0 .853 0l8.215-4.625a.7.7 0 0 0 .262-.255.7.7 0 0 0-.097-.349V7.337a.7.7 0 0 0-.096-.35.7.7 0 0 0-.263-.255l-8.216-4.623a.88.88 0 0 0-.851 0m-7.56 5.173H19.87c.225 0 .367.24.254.43l-7.928 13.387c-.053.089-.196.053-.196-.05v-8.77a.48.48 0 0 0-.252-.425l-7.787-4.38c-.094-.053-.055-.192.052-.192" }) }) }));
|
|
24
|
+
}
|
|
25
|
+
export function AgentStatusButton({ onOpenSettings }) {
|
|
26
|
+
const runtimeConfig = useNudgeUiRuntimeConfig();
|
|
27
|
+
const agent = useAgentClient(runtimeConfig.projectId);
|
|
28
|
+
return _jsx(AgentStatusButtonView, { agent: agent, onOpenSettings: onOpenSettings });
|
|
29
|
+
}
|
|
30
|
+
/** Renders the connection snapshot provided by the project client. */
|
|
31
|
+
export function AgentStatusButtonView({ agent, onOpenSettings }) {
|
|
32
|
+
const status = getAgentConnectionStatus(agent);
|
|
33
|
+
const connected = agent.paired || agent.pairedElsewhere;
|
|
34
|
+
const listening = connected && agent.listenerActive;
|
|
35
|
+
return (_jsx(Tooltip, { content: connected ? status.label : "Agent & MCP settings", stableTrigger: true, children: _jsxs(Button, { variant: "quiet", className: "agent-status-button", "aria-label": connected ? `MCP · ${status.label}` : "Settings", "data-test": "settings-button", "data-mcp-connected": connected ? "true" : "false", "data-mcp-listening": listening ? "true" : "false", onClick: () => onOpenSettings("mcp"), children: [connected ? _jsx(ConnectedAgentAvatar, { name: agent.agentClientName }) : _jsxs("span", { className: "agent-status-button__avatars", children: [_jsx(CodexAvatar, {}), _jsx(ClaudeAvatar, {}), _jsx(CursorAvatar, {})] }), _jsx("span", { className: "agent-status-button__label", children: connected ? "MCP" : "Settings" }), connected ? (_jsx("span", { className: "agent-status-button__dot", "data-test": "agent-status-dot", "aria-hidden": "true" })) : null] }) }));
|
|
36
|
+
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { InspectorShell } from "./InspectorShell.js";
|
|
3
3
|
import { CanvasWorkspace } from "../canvas/CanvasWorkspace.js";
|
|
4
4
|
import { CanvasElementOverlay } from "../canvas/CanvasElementOverlay.js";
|
|
5
5
|
import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
|
|
6
6
|
import { readNudgeUiEditorTarget } from "../../transport/editor.js";
|
|
7
|
+
import { TooltipGroup } from "../ui/Tooltip.js";
|
|
8
|
+
import { CommentsOverlay } from "../comments/CommentsOverlay.js";
|
|
7
9
|
export function AppShell() {
|
|
8
10
|
const canvasEnabled = useNudgeUiRuntimeConfig().capabilities.canvas;
|
|
9
11
|
const primaryUrl = typeof window === "undefined"
|
|
10
12
|
? null
|
|
11
13
|
: readNudgeUiEditorTarget(window.location.href);
|
|
12
|
-
return (_jsxs(
|
|
14
|
+
return (_jsxs(TooltipGroup, { children: [canvasEnabled ? _jsx(CanvasWorkspace, { primaryUrl: primaryUrl }) : null, canvasEnabled ? _jsx(CanvasElementOverlay, {}) : null, canvasEnabled ? _jsx(CommentsOverlay, {}) : null, _jsx(InspectorShell, {})] }));
|
|
13
15
|
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useSyncExternalStore } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { IconArrowBackUp } from "@tabler/icons-react";
|
|
4
|
+
import { isComponentChange, isElementChange, isTextContentChange, isTokenChange, useChanges, revertChange, } from "../changes/changesLog.js";
|
|
4
5
|
import { StaleChangeIndicator } from "../canvas/StaleChangeIndicator.js";
|
|
5
6
|
import { Button } from "../ui/Button.js";
|
|
6
7
|
import { Disclosure } from "../ui/Disclosure.js";
|
|
8
|
+
import { IconButton } from "../ui/IconButton.js";
|
|
9
|
+
import { Tooltip } from "../ui/Tooltip.js";
|
|
7
10
|
import { presentChange } from "../changes/presentation.js";
|
|
8
11
|
import { getStructuralChanges, getStructuralChangeDiagnostics, getStructuralDiagnosticRevision, revertStructuralChange, subscribeStructuralChanges, subscribeStructuralDiagnostics, } from "../projection/structuralProjection.js";
|
|
9
12
|
import { getRenderedInstanceChangeDiagnostics, getRenderedInstanceDiagnosticRevision, subscribeRenderedInstanceDiagnostics, } from "../projection/renderedInstance.js";
|
|
10
13
|
import { getTextContentChangeDiagnostics, getTextProjectionDiagnosticRevision, subscribeTextProjectionDiagnostics, } from "../projection/textProjection.js";
|
|
11
14
|
import { SketchChanges } from "../sketch/SketchChanges.js";
|
|
12
15
|
import { useSketchStore } from "../sketch/store.js";
|
|
16
|
+
import { useFrameComments } from "../comments/store.js";
|
|
17
|
+
import { countPromptChanges } from "../prompt/generatePrompt.js";
|
|
13
18
|
function groupChanges(changes) {
|
|
14
19
|
const map = new Map();
|
|
15
20
|
for (const change of changes) {
|
|
@@ -20,7 +25,6 @@ function groupChanges(changes) {
|
|
|
20
25
|
group = {
|
|
21
26
|
key,
|
|
22
27
|
label: presentation.groupLabel,
|
|
23
|
-
file: presentation.file,
|
|
24
28
|
changes: [],
|
|
25
29
|
};
|
|
26
30
|
map.set(key, group);
|
|
@@ -29,9 +33,6 @@ function groupChanges(changes) {
|
|
|
29
33
|
}
|
|
30
34
|
return Array.from(map.values());
|
|
31
35
|
}
|
|
32
|
-
function sourceFile(change) {
|
|
33
|
-
return change.target.sourceSite.src.split(":").slice(0, -2).join(":") || change.target.sourceSite.src;
|
|
34
|
-
}
|
|
35
36
|
function structuralParentLabel(change, side) {
|
|
36
37
|
const parent = side === "source" ? change.source.parent : change.destination.parent;
|
|
37
38
|
const tag = side === "source" ? change.presentation.sourceParentTag : change.presentation.destinationParentTag;
|
|
@@ -60,8 +61,26 @@ function structuralReasonLabel(reason) {
|
|
|
60
61
|
function structuralDiagnosticText(diagnostic) {
|
|
61
62
|
return `${diagnostic.document}: ${diagnostic.status}${diagnostic.reason ? ` (${structuralReasonLabel(diagnostic.reason)})` : ""}`;
|
|
62
63
|
}
|
|
64
|
+
function changeTargetLabel(change, presentation) {
|
|
65
|
+
if (isComponentChange(change))
|
|
66
|
+
return change.target.componentName;
|
|
67
|
+
if (isTextContentChange(change))
|
|
68
|
+
return change.target.sourceSite.cid;
|
|
69
|
+
if (isTokenChange(change))
|
|
70
|
+
return change.tokenName;
|
|
71
|
+
return isElementChange(change) ? change.cid : presentation.groupLabel;
|
|
72
|
+
}
|
|
73
|
+
function changeActionLabel(change, presentation) {
|
|
74
|
+
if (isTextContentChange(change))
|
|
75
|
+
return "Changed text";
|
|
76
|
+
return `Changed ${presentation.propertyLabel}`;
|
|
77
|
+
}
|
|
78
|
+
function RevertButton({ dataTest, dataProperty, onClick, }) {
|
|
79
|
+
return (_jsx(Tooltip, { content: "Revert", stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", size: "compact", className: "changes__revert", "data-test": dataTest, "data-property": dataProperty, label: "Revert change", onClick: onClick, children: _jsx(IconArrowBackUp, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) }) }));
|
|
80
|
+
}
|
|
63
81
|
export function ChangesLog({ onClearSession }) {
|
|
64
82
|
const changes = useChanges();
|
|
83
|
+
const comments = useFrameComments();
|
|
65
84
|
const sketchStore = useSketchStore();
|
|
66
85
|
const sketches = sketchStore.items;
|
|
67
86
|
const structuralChanges = useSyncExternalStore(subscribeStructuralChanges, getStructuralChanges, getStructuralChanges);
|
|
@@ -71,26 +90,27 @@ export function ChangesLog({ onClearSession }) {
|
|
|
71
90
|
useSyncExternalStore(subscribeRenderedInstanceDiagnostics, getRenderedInstanceDiagnosticRevision, getRenderedInstanceDiagnosticRevision);
|
|
72
91
|
useSyncExternalStore(subscribeTextProjectionDiagnostics, getTextProjectionDiagnosticRevision, getTextProjectionDiagnosticRevision);
|
|
73
92
|
const groups = useMemo(() => groupChanges(changes), [changes]);
|
|
74
|
-
const total = changes
|
|
75
|
-
|
|
93
|
+
const total = countPromptChanges(changes, structuralChanges, sketches.length);
|
|
94
|
+
const hasLoggedChanges = total > 0 || sketchStore.error !== null;
|
|
95
|
+
if (!hasLoggedChanges && comments.length === 0)
|
|
76
96
|
return null;
|
|
77
|
-
return (_jsxs(_Fragment, { children: [_jsx(Disclosure, { className: "changes", "data-test": "changes-log", triggerDataTest: "changes-toggle", title: "Changes", badge: total > 0 ? _jsx("span", { className: "changes__count", children: total }) : null, panelClassName: "changes__content", children: groups.length === 0 && structuralChanges.length === 0 && sketches.length === 0 && sketchStore.error === null ? (_jsx("div", { className: "changes__empty", "data-test": "changes-empty", children: "No changes yet" })) : (_jsxs(_Fragment, { children: [groups.map((group) => (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
return (_jsxs(_Fragment, { children: [hasLoggedChanges ? _jsx(Disclosure, { className: "changes", "data-test": "changes-log", triggerDataTest: "changes-toggle", title: "Changes", badge: total > 0 ? _jsx("span", { className: "changes__count", children: total }) : null, panelClassName: "changes__content", children: groups.length === 0 && structuralChanges.length === 0 && sketches.length === 0 && sketchStore.error === null ? (_jsx("div", { className: "changes__empty", "data-test": "changes-empty", children: "No changes yet" })) : (_jsxs(_Fragment, { children: [groups.map((group) => (_jsx("div", { className: "changes__group", "data-test": "changes-group", "data-cid": group.label, children: group.changes.map((change, i) => {
|
|
98
|
+
const presentation = presentChange(change);
|
|
99
|
+
const instanceDiagnostics = isElementChange(change) && change.scope === "rendered-instance"
|
|
100
|
+
&& change.instanceOverride
|
|
101
|
+
? getRenderedInstanceChangeDiagnostics(change.instanceOverride.id)
|
|
102
|
+
: [];
|
|
103
|
+
const textDiagnostics = change.kind === "text-content"
|
|
104
|
+
? getTextContentChangeDiagnostics(change.id)
|
|
105
|
+
: [];
|
|
106
|
+
return (_jsxs("div", { className: "changes__row", "data-test": "change-row", "data-property": presentation.property, children: [_jsx("span", { className: "changes__target", "data-test": "change-target", children: changeTargetLabel(change, presentation) }), _jsx("span", { className: "changes__action", "data-test": "change-action", children: changeActionLabel(change, presentation) }), _jsxs("span", { className: "changes__metadata", "aria-hidden": "true", children: [_jsx("span", { className: "changes__prop", children: presentation.propertyLabel }), _jsxs("span", { className: "changes__value", children: [_jsx("span", { className: "changes__before", children: presentation.before }), _jsx("span", { className: "changes__arrow", children: "\u2192" }), _jsx("span", { className: "changes__after", children: presentation.after })] }), presentation.scope ? (_jsx("span", { className: "changes__scope", "data-test": "change-scope", children: presentation.scope === "source-site" ? "All outputs at source site" : "This rendered item only" })) : null, presentation.evidence ? (_jsx("span", { className: "changes__evidence", "data-test": "change-evidence", children: presentation.evidence })) : null, _jsx(StaleChangeIndicator, { change: change }), instanceDiagnostics.map((diagnostic) => (_jsxs("span", { className: "changes__diagnostic", "data-test": "instance-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, children: [diagnostic.document, ": ", diagnostic.status] }, `${diagnostic.document}:${diagnostic.status}`))), textDiagnostics.map((diagnostic) => (_jsxs("span", { className: "changes__diagnostic", "data-test": "text-projection-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, children: [diagnostic.document, ": ", diagnostic.status] }, `${diagnostic.document}:${diagnostic.status}`)))] }), _jsx(RevertButton, { dataTest: "change-revert", dataProperty: presentation.property, onClick: () => revertChange(change) })] }, `${group.key}\u0000${presentation.property}\u0000${i}`));
|
|
107
|
+
}) }, group.key))), structuralChanges.map((change) => (_jsx("div", { className: "changes__group", "data-test": "dom-change", "data-cid": change.target.sourceSite.cid, children: _jsxs("div", { className: "changes__row", "data-test": "dom-change-row", "data-action": change.kind, children: [_jsx("span", { className: "changes__target", "data-test": "structural-target", children: change.target.sourceSite.cid }), _jsx("span", { className: "changes__action", "data-test": "structural-action", children: change.kind === "move" ? "Moved" : "Deleted" }), _jsxs("span", { className: "changes__metadata", "aria-hidden": "true", children: [_jsxs("span", { className: "changes__source-site", "data-test": "structural-source-site", children: ["Source site: ", change.target.sourceSite.cid] }), _jsx("span", { className: "changes__prop", children: change.kind === "move" ? "Move in DOM" : "Delete from DOM" }), _jsxs("span", { className: "changes__value", children: [_jsx("span", { className: "changes__before", children: change.kind === "move"
|
|
108
|
+
? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
|
|
109
|
+
? `${change.presentation.sourceParentTag} position ${change.presentation.fromIndex + 1}`
|
|
110
|
+
: `${structuralParentLabel(change, "source")} position ${change.presentation.fromIndex + 1}`
|
|
111
|
+
: "Visible" }), _jsx("span", { className: "changes__arrow", children: "\u2192" }), _jsx("span", { className: "changes__after", children: change.kind === "move"
|
|
112
|
+
? structuralParentIdentityKey(change.source.parent) === structuralParentIdentityKey(change.destination.parent)
|
|
113
|
+
? `${change.presentation.destinationParentTag} position ${change.presentation.toIndex + 1}`
|
|
114
|
+
: `${structuralParentLabel(change, "destination")} position ${change.presentation.toIndex + 1}`
|
|
115
|
+
: "Removed" })] }), _jsx("span", { className: "changes__scope", "data-test": "structural-scope", children: "This rendered item only" }), getStructuralChangeDiagnostics(change.id).map((diagnostic) => (_jsx("span", { className: "changes__diagnostic", "data-test": "structural-diagnostic", "data-document": diagnostic.document, "data-status": diagnostic.status, "data-reason": diagnostic.reason, children: structuralDiagnosticText(diagnostic) }, `${diagnostic.document}:${diagnostic.status}:${diagnostic.reason ?? ""}`)))] }), _jsx(RevertButton, { dataTest: "dom-change-revert", onClick: () => revertStructuralChange(change.id) })] }) }, change.id))), _jsx(SketchChanges, {})] })) }) : null, onClearSession ? (_jsx("div", { className: "changes__session-action", "data-test": "session-actions", children: _jsx(Button, { variant: "secondary", type: "button", "data-test": "clear-session", onClick: onClearSession, children: "Clear Changes" }) })) : null] }));
|
|
96
116
|
}
|
|
@@ -1,32 +1,52 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Menu } from "@base-ui/react/menu";
|
|
3
|
+
import { iterationId } from "../canvas/frameContent.js";
|
|
2
4
|
import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
-
import { IconCheck, IconCopy, IconPlugConnected, IconSend, } from "@tabler/icons-react";
|
|
5
|
+
import { IconArtboard, IconBoltFilled, IconCheck, IconChevronDown, IconCopy, IconPlugConnected, IconSend, } from "@tabler/icons-react";
|
|
4
6
|
import { useChanges } from "../changes/changesLog.js";
|
|
5
|
-
import {
|
|
6
|
-
import { copyToClipboard } from "../prompt/copyToClipboard.js";
|
|
7
|
+
import { countPromptChanges } from "../prompt/generatePrompt.js";
|
|
7
8
|
import { loadCustomInstructions, saveCustomInstructions } from "../prompt/promptSettings.js";
|
|
8
9
|
import { getAgentConnectionStatus } from "../agent/connectionStatus.js";
|
|
9
10
|
import { SettingsDialog } from "../settings/SettingsDialog.js";
|
|
10
11
|
import { Button } from "../ui/Button.js";
|
|
12
|
+
import { portalContainer } from "../ui/portalContainer.js";
|
|
11
13
|
import { StatusCallout } from "../ui/StatusCallout.js";
|
|
12
14
|
import { getStructuralChanges, subscribeStructuralChanges } from "../projection/structuralProjection.js";
|
|
13
15
|
import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
|
|
14
|
-
import {
|
|
16
|
+
import { getAgentClient, useAgentClient } from "../agent/client.js";
|
|
15
17
|
import { createAgentPresentationAdapter } from "../canvas/agentPresentation.js";
|
|
16
|
-
import {
|
|
17
|
-
import { getClipboardHandoffRevision, getLastClipboardReconciledCount,
|
|
18
|
-
import { useSketches,
|
|
19
|
-
import { createSketchAttachments, createSketchHandoffSnapshot, sketchMetadataForHandoff, } from "../sketch/handoff.js";
|
|
18
|
+
import { verifyAndReconcileAgentDispatch } from "../agent/verification.js";
|
|
19
|
+
import { getClipboardHandoffRevision, getLastClipboardReconciledCount, subscribeClipboardHandoff, } from "../prompt/clipboardHandoff.js";
|
|
20
|
+
import { useSketches, settleSketchDispatch } from "../sketch/store.js";
|
|
20
21
|
import { SketchLayersPanel } from "../sketch/SketchLayersPanel.js";
|
|
21
22
|
import { isSendPromptShortcut, SEND_PROMPT_HOTKEY_EVENT } from "./shortcuts.js";
|
|
23
|
+
import { getFocusedCardId, getSelectedCardId, useCanvasCards, useFocusedCardId, useSelectedCardId } from "../canvas/canvasStore.js";
|
|
24
|
+
import { getEditableCardId, sketchBelongsToCard, useDrafts } from "../drafts/store.js";
|
|
25
|
+
import { copyHandoff, deliverHandoff, prepareHandoff, prepareIterationImplementationPrompt } from "../workspace/handoff.js";
|
|
26
|
+
import { copyToClipboard } from "../prompt/copyToClipboard.js";
|
|
27
|
+
import { useFrameComments } from "../comments/store.js";
|
|
28
|
+
import { reconcileComments } from "../comments/verification.js";
|
|
29
|
+
import { getRegisteredFrames } from "../canvas/projection.js";
|
|
22
30
|
export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, settingsSection: controlledSettingsSection, onOpenSettings, onSettingsOpenChange, } = {}) {
|
|
23
31
|
const changes = useChanges();
|
|
32
|
+
const comments = useFrameComments();
|
|
33
|
+
useDrafts();
|
|
24
34
|
const sketches = useSketches();
|
|
25
|
-
const
|
|
35
|
+
const selectedDraftCardId = useSelectedCardId();
|
|
36
|
+
const focusedDraftCardId = useFocusedCardId();
|
|
37
|
+
const activeDraftCardId = getEditableCardId(selectedDraftCardId, focusedDraftCardId);
|
|
38
|
+
const canvasCards = useCanvasCards();
|
|
39
|
+
const activeContent = canvasCards.find((card) => card.id === activeDraftCardId)?.content;
|
|
40
|
+
const activeArtifactId = iterationId(activeContent);
|
|
41
|
+
const visibleSketches = sketches.filter((item) => sketchBelongsToCard(item.document, activeDraftCardId));
|
|
42
|
+
const pendingSketches = visibleSketches.filter((item) => item.status === "pending");
|
|
26
43
|
const structuralChanges = useSyncExternalStore(subscribeStructuralChanges, getStructuralChanges, getStructuralChanges);
|
|
27
44
|
useSyncExternalStore(subscribeClipboardHandoff, getClipboardHandoffRevision, getClipboardHandoffRevision);
|
|
28
45
|
const reconciledCount = getLastClipboardReconciledCount();
|
|
29
46
|
const [copied, setCopied] = useState(false);
|
|
47
|
+
const [preparing, setPreparing] = useState(false);
|
|
48
|
+
const handoffBusy = useRef(false);
|
|
49
|
+
const [saveMessage, setSaveMessage] = useState(null);
|
|
30
50
|
const [agentCompletionStatus, setAgentCompletionStatus] = useState(null);
|
|
31
51
|
const [sketchFallback, setSketchFallback] = useState(null);
|
|
32
52
|
const [localSettingsOpen, setLocalSettingsOpen] = useState(false);
|
|
@@ -39,8 +59,13 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
|
|
|
39
59
|
const agent = useAgentClient(runtimeConfig.projectId, agentClient);
|
|
40
60
|
const agentRef = useRef(agent);
|
|
41
61
|
agentRef.current = agent;
|
|
42
|
-
const hasChanges = changes
|
|
43
|
-
|
|
62
|
+
const hasChanges = Boolean(activeArtifactId) || countPromptChanges(changes, structuralChanges, pendingSketches.length) + comments.length > 0;
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!copied)
|
|
65
|
+
return;
|
|
66
|
+
const timer = window.setTimeout(() => setCopied(false), 1500);
|
|
67
|
+
return () => window.clearTimeout(timer);
|
|
68
|
+
}, [copied]);
|
|
44
69
|
useEffect(() => {
|
|
45
70
|
setCustomInstructions(loadCustomInstructions(runtimeConfig.projectId));
|
|
46
71
|
}, [runtimeConfig.projectId]);
|
|
@@ -61,6 +86,13 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
|
|
|
61
86
|
return;
|
|
62
87
|
let active = true;
|
|
63
88
|
void verifyAndReconcileAgentDispatch(revision)
|
|
89
|
+
.then(async (removed) => {
|
|
90
|
+
for (const [, iframe] of getRegisteredFrames()) {
|
|
91
|
+
if (iframe.contentDocument)
|
|
92
|
+
await reconcileComments(iframe.contentDocument);
|
|
93
|
+
}
|
|
94
|
+
return removed;
|
|
95
|
+
})
|
|
64
96
|
.then((removed) => {
|
|
65
97
|
if (!active)
|
|
66
98
|
return;
|
|
@@ -95,95 +127,116 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
|
|
|
95
127
|
&& !connecting
|
|
96
128
|
&& !working;
|
|
97
129
|
const canSend = agent.paired && agent.listenerActive && !working;
|
|
98
|
-
const
|
|
130
|
+
const changeCount = countPromptChanges(changes, structuralChanges, canSend ? pendingSketches.length : 0) + comments.length;
|
|
131
|
+
const disabled = preparing || connecting || working || (!canConnect && !hasChanges);
|
|
99
132
|
const agentStatus = getAgentConnectionStatus(agent);
|
|
100
133
|
const statusAction = agentStatus.action;
|
|
101
|
-
const label =
|
|
102
|
-
? "
|
|
103
|
-
:
|
|
104
|
-
? "
|
|
105
|
-
:
|
|
106
|
-
? "
|
|
107
|
-
:
|
|
108
|
-
? "
|
|
109
|
-
:
|
|
110
|
-
? "
|
|
111
|
-
:
|
|
134
|
+
const label = preparing
|
|
135
|
+
? "Saving iteration…"
|
|
136
|
+
: copied
|
|
137
|
+
? "Copied!"
|
|
138
|
+
: connecting
|
|
139
|
+
? "Connecting…"
|
|
140
|
+
: working
|
|
141
|
+
? "Agent working…"
|
|
142
|
+
: canConnect && !activeArtifactId
|
|
143
|
+
? "Connect agent"
|
|
144
|
+
: canSend && !activeArtifactId
|
|
145
|
+
? "Send prompt"
|
|
146
|
+
: "Copy prompt";
|
|
112
147
|
const icon = copied
|
|
113
148
|
? _jsx(IconCheck, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
|
|
114
|
-
: canConnect || connecting
|
|
149
|
+
: !activeArtifactId && (canConnect || connecting)
|
|
115
150
|
? _jsx(IconPlugConnected, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
|
|
116
|
-
: canSend || working
|
|
151
|
+
: !activeArtifactId && (canSend || working)
|
|
117
152
|
? _jsx(IconSend, { size: "var(--icon-size-small)", stroke: 1.8, "aria-hidden": "true" })
|
|
118
153
|
: _jsx(IconCopy, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" });
|
|
119
154
|
async function onClick() {
|
|
120
|
-
if (disabled)
|
|
121
|
-
return;
|
|
122
|
-
if (canConnect) {
|
|
123
|
-
await agentClient.connect();
|
|
155
|
+
if (disabled || handoffBusy.current)
|
|
124
156
|
return;
|
|
157
|
+
handoffBusy.current = true;
|
|
158
|
+
setPreparing(Boolean(activeArtifactId) && !canConnect);
|
|
159
|
+
try {
|
|
160
|
+
await handoff();
|
|
125
161
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
});
|
|
149
|
-
if (response) {
|
|
150
|
-
if (sketchHandoff) {
|
|
151
|
-
await markSketchesHandingOff(sketchHandoff.entries.map((entry) => ({ id: entry.id, revision: entry.revision })), revision, sketchHandoff.localBatchId, response.request.requestId);
|
|
152
|
-
}
|
|
153
|
-
return;
|
|
162
|
+
finally {
|
|
163
|
+
handoffBusy.current = false;
|
|
164
|
+
setPreparing(false);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function copyIterationPrompt(target) {
|
|
168
|
+
if (!activeArtifactId || disabled || handoffBusy.current)
|
|
169
|
+
return;
|
|
170
|
+
handoffBusy.current = true;
|
|
171
|
+
setCopied(false);
|
|
172
|
+
setPreparing(true);
|
|
173
|
+
try {
|
|
174
|
+
const request = {
|
|
175
|
+
cardId: getEditableCardId(getSelectedCardId(), getFocusedCardId()),
|
|
176
|
+
hints: { framework: runtimeConfig.framework, stylingSystem: runtimeConfig.stylingSystem },
|
|
177
|
+
customInstructions,
|
|
178
|
+
pendingSketches,
|
|
179
|
+
};
|
|
180
|
+
if (target === "application") {
|
|
181
|
+
const prompt = await prepareIterationImplementationPrompt(request);
|
|
182
|
+
try {
|
|
183
|
+
await copyToClipboard(prompt);
|
|
154
184
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
discardAgentDispatch(revision);
|
|
158
|
-
const latestAgent = agentClient.getSnapshot();
|
|
159
|
-
setSketchFallback({
|
|
160
|
-
prompt: text,
|
|
161
|
-
error: latestAgent.request?.error
|
|
162
|
-
?? latestAgent.error
|
|
163
|
-
?? "The agent did not accept the sketch attachments.",
|
|
164
|
-
});
|
|
165
|
-
return;
|
|
185
|
+
catch {
|
|
186
|
+
throw new Error("The iteration was saved, but the prompt could not be copied. Try again.");
|
|
166
187
|
}
|
|
167
188
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
await
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
});
|
|
176
|
-
return;
|
|
189
|
+
else {
|
|
190
|
+
const prepared = await prepareHandoff(request);
|
|
191
|
+
try {
|
|
192
|
+
await copyHandoff(prepared);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
throw new Error("The iteration was saved, but the prompt could not be copied. Try again.");
|
|
177
196
|
}
|
|
178
197
|
}
|
|
179
|
-
|
|
198
|
+
setSaveMessage(null);
|
|
199
|
+
setCopied(true);
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
setSaveMessage(error instanceof Error ? error.message : "The prompt could not be copied. Try again.");
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
handoffBusy.current = false;
|
|
206
|
+
setPreparing(false);
|
|
180
207
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
208
|
+
}
|
|
209
|
+
async function handoff() {
|
|
210
|
+
if (disabled)
|
|
211
|
+
return;
|
|
212
|
+
if (canConnect) {
|
|
213
|
+
await agentClient.connect();
|
|
214
|
+
return;
|
|
184
215
|
}
|
|
216
|
+
let prepared;
|
|
217
|
+
try {
|
|
218
|
+
prepared = await prepareHandoff({
|
|
219
|
+
cardId: getEditableCardId(getSelectedCardId(), getFocusedCardId()),
|
|
220
|
+
hints: { framework: runtimeConfig.framework, stylingSystem: runtimeConfig.stylingSystem },
|
|
221
|
+
customInstructions,
|
|
222
|
+
pendingSketches,
|
|
223
|
+
});
|
|
224
|
+
setSaveMessage(null);
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
setSaveMessage(error instanceof Error ? error.message : "The HTML iteration could not be saved.");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
setAgentCompletionStatus(null);
|
|
231
|
+
setSketchFallback(null);
|
|
232
|
+
const outcome = await deliverHandoff(prepared, runtimeConfig.projectId, canSend ? agentClient : null);
|
|
233
|
+
if (outcome.kind === "sketch-failed") {
|
|
234
|
+
setSketchFallback({ handoff: outcome.handoff, error: outcome.error });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (outcome.kind === "sent")
|
|
238
|
+
return;
|
|
185
239
|
setCopied(true);
|
|
186
|
-
window.setTimeout(() => setCopied(false), 1500);
|
|
187
240
|
}
|
|
188
241
|
useEffect(() => {
|
|
189
242
|
function onKeydown(event) {
|
|
@@ -231,12 +284,8 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
|
|
|
231
284
|
}
|
|
232
285
|
openMcpConnection();
|
|
233
286
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
recordClipboardHandoff(changes, structuralChanges);
|
|
238
|
-
}
|
|
239
|
-
setSketchFallback(null);
|
|
240
|
-
}).catch(() => undefined);
|
|
287
|
+
const promptButton = (_jsxs(Button, { variant: "primary", className: "copy-prompt__main", "data-test": "copy-prompt", type: "button", disabled: disabled, "data-copied": copied ? "true" : "false", "data-agent-state": agent.state, "aria-busy": preparing || working || connecting ? "true" : undefined, onClick: activeArtifactId ? undefined : onClick, children: [icon, label, changeCount > 0 ? (_jsx("span", { className: "copy-prompt__change-count", "data-test": "copy-prompt-change-count", children: changeCount })) : null, activeArtifactId ? _jsx(IconChevronDown, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) : null] }));
|
|
288
|
+
return (_jsxs("div", { className: "copy-prompt__stack", "data-test": "copy-prompt-control", children: [activeArtifactId ? (_jsxs(Menu.Root, { modal: false, children: [_jsx(Menu.Trigger, { render: promptButton }), _jsx(Menu.Portal, { container: portalContainer(), children: _jsx(Menu.Positioner, { className: "inspector-popover__positioner", side: "bottom", align: "start", sideOffset: 6, children: _jsxs(Menu.Popup, { className: "inspector-popover__popup copy-prompt__menu", "data-test": "copy-prompt-menu", children: [_jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-prompt", label: "Copy iteration prompt", onClick: () => void copyIterationPrompt("iteration"), children: [_jsx(IconArtboard, { className: "copy-prompt__menu-icon", size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy iteration prompt" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Continue editing this HTML iteration." })] })] }), _jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-implementation", label: "Copy prompt for live app", onClick: () => void copyIterationPrompt("application"), children: [_jsx(IconBoltFilled, { className: "copy-prompt__menu-icon copy-prompt__menu-icon--live", size: "var(--icon-size-small)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy prompt for live app" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Implement design in real app" })] })] })] }) }) })] }, activeDraftCardId)) : promptButton, _jsx(SketchLayersPanel, {}), statusAction || agentStatus.kind === "connected-not-listening" ? (_jsx(StatusCallout, { className: "copy-prompt__agent-status", tone: agentStatus.tone, "data-test": "agent-connection-status", children: _jsxs("div", { className: "copy-prompt__agent-status-content", children: [_jsx("span", { role: "status", children: agentStatus.label }), statusAction ? (_jsx(Button, { size: "compact", variant: "quiet", "data-test": "agent-status-action", "data-action": statusAction.kind, type: "button", onClick: () => void handleAgentStatusAction(), children: statusAction.label })) : null] }) })) : null, reconciledCount > 0 ? (_jsxs("p", { className: "copy-prompt__hint copy-prompt__hint--centered", "data-test": "clipboard-reconciled-hint", role: "status", children: [reconciledCount, " Implemented changes"] })) : null, saveMessage ? (_jsx("p", { className: "copy-prompt__hint copy-prompt__hint--centered", "data-test": "iteration-save-hint", role: "status", children: saveMessage })) : null, agentCompletionStatus === "verified" ? (_jsx("p", { className: "copy-prompt__hint copy-prompt__hint--centered copy-prompt__hint--implemented", "data-test": "agent-verified-hint", role: "status", children: "Changes implemented" })) : null, sketchFallback ? (_jsxs(StatusCallout, { className: "copy-prompt__sketch-fallback", tone: "warning", "data-test": "sketch-dispatch-error", children: [_jsx("span", { children: sketchFallback.error }), _jsx(Button, { size: "compact", variant: "secondary", type: "button", "data-test": "sketch-copy-fallback", onClick: () => {
|
|
289
|
+
void copyHandoff(sketchFallback.handoff).then(() => setSketchFallback(null)).catch(() => undefined);
|
|
241
290
|
}, children: "Copy prompt" })] })) : null, _jsx(SettingsDialog, { open: settingsOpen, initialSection: settingsSection, value: customInstructions, onValueChange: handleCustomInstructionsChange, onOpenChange: setSettingsOpen, projectId: runtimeConfig.projectId, origin: window.location.origin, snapshot: agent, onConnect: () => agentClient.connect(), onTakeOver: () => agentClient.takeOver(), onDisconnect: () => agentClient.disconnect(), onCheckAgain: () => agentClient.checkConnection() })] }));
|
|
242
291
|
}
|