nudge-ui 0.2.4 → 0.2.5
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/dist/cli/agentCommand.js +4 -28
- package/dist/client.mjs +863 -213
- package/dist/hosts/next/wrapper.js +1 -7
- package/dist/hosts/projectBridge.js +3 -17
- package/dist/inspector/agent/McpConnectionDialog.js +7 -3
- package/dist/inspector/agent/activity.d.ts +1 -1
- package/dist/inspector/agent/activity.js +3 -2
- package/dist/inspector/agent/client.d.ts +2 -2
- package/dist/inspector/agent/client.js +28 -29
- package/dist/inspector/agent/connectionStatus.d.ts +3 -1
- package/dist/inspector/agent/connectionStatus.js +25 -0
- package/dist/inspector/agent/httpTransport.js +31 -147
- package/dist/inspector/agent/protocol.d.ts +1 -1
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +1 -1
- package/dist/inspector/agent/verification.js +7 -4
- package/dist/inspector/boot.d.ts +3 -0
- package/dist/inspector/boot.js +297 -0
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +2 -1
- package/dist/inspector/canvas/AgentActivityOverlay.js +6 -3
- package/dist/inspector/canvas/CanvasCard.js +21 -12
- package/dist/inspector/canvas/CanvasElementOverlay.js +82 -127
- package/dist/inspector/canvas/CanvasToolbar.js +2 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +74 -97
- package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +3 -2
- package/dist/inspector/canvas/LockedWorkspaceNotice.js +10 -6
- package/dist/inspector/canvas/PageCard.d.ts +10 -0
- package/dist/inspector/canvas/PageCard.js +51 -0
- package/dist/inspector/canvas/SketchToolOptions.d.ts +5 -0
- package/dist/inspector/canvas/SketchToolOptions.js +86 -0
- package/dist/inspector/canvas/StaleChangeIndicator.d.ts +1 -1
- package/dist/inspector/canvas/StaleChangeIndicator.js +1 -1
- package/dist/inspector/canvas/ToolOptionsDrawer.d.ts +21 -0
- package/dist/inspector/canvas/ToolOptionsDrawer.js +77 -0
- package/dist/inspector/canvas/activeCanvasDocument.d.ts +3 -2
- package/dist/inspector/canvas/assets/eraser.svg +28 -0
- package/dist/inspector/canvas/assets/pen.svg +25 -0
- package/dist/inspector/canvas/canvasFrame.d.ts +16 -0
- package/dist/inspector/canvas/canvasFrame.js +10 -0
- package/dist/inspector/canvas/canvasGestures.d.ts +0 -1
- package/dist/inspector/canvas/canvasGestures.js +0 -6
- package/dist/inspector/canvas/canvasStore.d.ts +6 -0
- package/dist/inspector/canvas/canvasStore.js +36 -1
- package/dist/inspector/canvas/frameConnection.d.ts +48 -0
- package/dist/inspector/canvas/frameConnection.js +73 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +18 -1
- package/dist/inspector/canvas/frameProtocol.js +36 -0
- package/dist/inspector/canvas/pageSurface.d.ts +11 -0
- package/dist/inspector/canvas/pageSurface.js +34 -0
- package/dist/inspector/canvas/projection.d.ts +19 -9
- package/dist/inspector/canvas/projection.js +58 -31
- package/dist/inspector/canvas/rendererBootstrap.js +4 -1
- package/dist/inspector/canvas/rendererElementSelector.js +9 -1
- package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
- package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
- package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
- package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
- package/dist/inspector/canvas/rendererStylesheet.js +15 -0
- package/dist/inspector/canvas/sessionStore.d.ts +2 -2
- package/dist/inspector/canvas/sessionStore.js +12 -9
- package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
- package/dist/inspector/canvas/staleChangeDetector.js +10 -22
- package/dist/inspector/changes/codecs.d.ts +1 -0
- package/dist/inspector/changes/codecs.js +20 -5
- package/dist/inspector/changes/draftPreview.d.ts +12 -0
- package/dist/inspector/changes/draftPreview.js +133 -0
- package/dist/inspector/changes/editModel.d.ts +8 -0
- package/dist/inspector/changes/editModel.js +8 -2
- package/dist/inspector/changes/managedStyleProjection.js +2 -1
- package/dist/inspector/changes/previewDiagnostics.js +1 -1
- package/dist/inspector/changes/types.d.ts +11 -1
- package/dist/inspector/comments/CommentsOverlay.js +18 -16
- package/dist/inspector/comments/store.js +3 -2
- package/dist/inspector/comments/verification.js +1 -1
- package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
- package/dist/inspector/componentSemantics/changeAction.js +1 -1
- package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
- package/dist/inspector/componentSemantics/textBinding.js +4 -45
- package/dist/inspector/drafts/draftStore.d.ts +109 -0
- package/dist/inspector/drafts/draftStore.js +451 -0
- package/dist/inspector/drafts/model.d.ts +9 -1
- package/dist/inspector/drafts/model.js +5 -1
- package/dist/inspector/drafts/store.d.ts +53 -15
- package/dist/inspector/drafts/store.js +143 -169
- package/dist/inspector/index.d.ts +2 -4
- package/dist/inspector/index.js +2 -264
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
- package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
- package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
- package/dist/inspector/inspection/bridge.d.ts +1 -1
- package/dist/inspector/inspection/bridge.js +2 -1
- package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
- package/dist/inspector/inspection/browserCssInspection.js +28 -6
- package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
- package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
- package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
- package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
- package/dist/inspector/iterations/capture.d.ts +2 -1
- package/dist/inspector/iterations/client.d.ts +2 -1
- package/dist/inspector/iterations/coordinator.d.ts +4 -3
- package/dist/inspector/iterations/coordinator.js +5 -3
- package/dist/inspector/iterations/prompt.js +9 -8
- package/dist/inspector/overlay/interactionStyles.js +17 -0
- package/dist/inspector/pageInspector.d.ts +42 -0
- package/dist/inspector/pageInspector.js +342 -0
- package/dist/inspector/projection/draftProjection.d.ts +10 -2
- package/dist/inspector/projection/draftProjection.js +9 -1
- package/dist/inspector/projection/managedStylesheet.js +24 -4
- package/dist/inspector/projection/renderedInstance.js +7 -1
- package/dist/inspector/projection/structuralProjection.d.ts +0 -9
- package/dist/inspector/projection/structuralProjection.js +14 -44
- package/dist/inspector/projection/textProjection.d.ts +8 -2
- package/dist/inspector/projection/textProjection.js +21 -13
- package/dist/inspector/prompt/clipboardHandoff.d.ts +9 -0
- package/dist/inspector/prompt/clipboardHandoff.js +42 -16
- package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
- package/dist/inspector/prompt/generatePrompt.js +243 -147
- package/dist/inspector/prompt/promptDocument.d.ts +3 -0
- package/dist/inspector/prompt/promptDocument.js +8 -0
- package/dist/inspector/prompt/promptSettings.js +7 -5
- package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
- package/dist/inspector/runtime/inspectorStorage.js +42 -0
- package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
- package/dist/inspector/runtime/ownedStylesheets.js +72 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
- package/dist/inspector/runtime/runtimeConfig.js +10 -1
- package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
- package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
- package/dist/inspector/selection/editScope.d.ts +1 -0
- package/dist/inspector/selection/editScope.js +13 -2
- package/dist/inspector/shell/AgentStatusButton.js +2 -2
- package/dist/inspector/shell/AppShell.js +6 -2
- package/dist/inspector/shell/ChangesLog.js +27 -9
- package/dist/inspector/shell/CopyPromptButton.js +60 -54
- package/dist/inspector/shell/EmptyState.js +3 -3
- package/dist/inspector/shell/InspectorShell.d.ts +0 -3
- package/dist/inspector/shell/InspectorShell.js +21 -13
- package/dist/inspector/shell/PeekOriginalButton.js +3 -3
- package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
- package/dist/inspector/shell/interactionStatePreview.js +112 -0
- package/dist/inspector/shell/originalPreview.d.ts +3 -2
- package/dist/inspector/shell/originalPreview.js +10 -37
- package/dist/inspector/shell/stateValue.js +2 -3
- package/dist/inspector/sketch/SketchEditor.js +5 -4
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
- package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
- package/dist/inspector/sketch/SketchOverlay.js +139 -263
- package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
- package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
- package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
- package/dist/inspector/sketch/SketchThumbnail.js +18 -0
- package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
- package/dist/inspector/sketch/SketchWorkspace.js +69 -34
- package/dist/inspector/sketch/capture.js +19 -3
- package/dist/inspector/sketch/copySketch.d.ts +10 -0
- package/dist/inspector/sketch/copySketch.js +52 -0
- package/dist/inspector/sketch/drawingTools.d.ts +17 -0
- package/dist/inspector/sketch/drawingTools.js +47 -0
- package/dist/inspector/sketch/frameHeading.d.ts +9 -0
- package/dist/inspector/sketch/frameHeading.js +30 -0
- package/dist/inspector/sketch/freehand.js +3 -3
- package/dist/inspector/sketch/geometry.d.ts +6 -0
- package/dist/inspector/sketch/geometry.js +19 -0
- package/dist/inspector/sketch/history.d.ts +30 -0
- package/dist/inspector/sketch/history.js +58 -0
- package/dist/inspector/sketch/interaction.d.ts +1 -3
- package/dist/inspector/sketch/interaction.js +6 -9
- package/dist/inspector/sketch/model.d.ts +35 -1
- package/dist/inspector/sketch/model.js +116 -61
- package/dist/inspector/sketch/persistence.d.ts +14 -0
- package/dist/inspector/sketch/persistence.js +63 -7
- package/dist/inspector/sketch/raster.d.ts +6 -2
- package/dist/inspector/sketch/raster.js +43 -17
- package/dist/inspector/sketch/store.d.ts +1 -1
- package/dist/inspector/sketch/store.js +1 -1
- package/dist/inspector/sketch/videoFrames.d.ts +6 -0
- package/dist/inspector/sketch/videoFrames.js +81 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +2 -1
- package/dist/inspector/styleEditors/BorderEditor.js +30 -130
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
- package/dist/inspector/styleEditors/LayoutSection.js +2 -2
- package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
- package/dist/inspector/styleEditors/borderModel.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.js +2 -3
- package/dist/inspector/tokens/TokenField.js +34 -3
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/editActions.d.ts +3 -3
- package/dist/inspector/tokens/editActions.js +9 -3
- package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
- package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
- package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
- package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
- package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
- package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
- package/dist/inspector/tokens/resolution.d.ts +6 -3
- package/dist/inspector/tokens/resolution.js +91 -112
- package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
- package/dist/inspector/tokens/tokenEdits.js +1 -1
- package/dist/inspector/ui/InlineStyleWarning.js +2 -2
- package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
- package/dist/inspector/ui/InspectorStyle.js +26 -0
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +3 -3
- package/dist/inspector/ui/Toast.d.ts +14 -0
- package/dist/inspector/ui/Toast.js +30 -0
- package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
- package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
- package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
- package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
- package/dist/inspector/ui/customCursors.d.ts +5 -0
- package/dist/inspector/ui/customCursors.js +32 -6
- package/dist/inspector/ui/styles.js +12 -10
- package/dist/inspector/workspace/commands.js +1 -2
- package/dist/inspector/workspace/controller.js +1 -2
- package/dist/inspector/workspace/handoff.js +3 -2
- package/dist/inspector/workspace/sentItems.d.ts +3 -6
- package/dist/inspector/workspace/sentItems.js +6 -24
- package/dist/inspector/workspace/timeline.d.ts +24 -10
- package/dist/inspector/workspace/timeline.js +118 -96
- package/dist/internal/agent-protocol.d.ts +50 -13
- package/dist/internal/agent-protocol.js +145 -19
- package/dist/nudge-ui.mjs +81 -65
- package/dist/project/mcpPackage.d.ts +8 -0
- package/dist/project/mcpPackage.js +60 -0
- package/package.json +12 -2
- package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
- package/dist/inspector/canvas/measurementProjection.js +0 -16
- package/dist/inspector/changes/changesLog.d.ts +0 -49
- package/dist/inspector/changes/changesLog.js +0 -255
- package/dist/inspector/changes/draftChanges.d.ts +0 -58
- package/dist/inspector/changes/draftChanges.js +0 -304
- package/dist/inspector/conformance/projection.d.ts +0 -2
- package/dist/inspector/conformance/projection.js +0 -3
- package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
- package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
- package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
- package/dist/inspector/overlay/InspectorOverlay.js +0 -332
- package/dist/inspector/overlay/elementSelector.d.ts +0 -1
- package/dist/inspector/overlay/elementSelector.js +0 -111
- package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
- package/dist/inspector/sketch/SketchChanges.js +0 -72
- package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
- package/dist/inspector/sketch/SketchEntryButton.js +0 -11
- package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
- package/dist/inspector/sketch/SketchLayersPanel.js +0 -71
|
@@ -3,6 +3,7 @@ import { IconMessageCircle, IconMessageCircleFilled, IconPointer2, IconSketching
|
|
|
3
3
|
import { IconButton } from "../ui/IconButton.js";
|
|
4
4
|
import { Tooltip } from "../ui/Tooltip.js";
|
|
5
5
|
import { DesignPointerFilledIcon, SelectPlayFilledIcon, SelectPlayOutlineIcon, SketchThickIcon, } from "./CanvasToolIcons.js";
|
|
6
|
+
import { SketchToolOptions } from "./SketchToolOptions.js";
|
|
6
7
|
export const CANVAS_ZOOM_LEVELS = [0.25, 0.5, 0.9, 1];
|
|
7
8
|
export function closestCanvasZoomLevel(zoom) {
|
|
8
9
|
let closest = CANVAS_ZOOM_LEVELS[0];
|
|
@@ -24,5 +25,5 @@ function ToolButton({ tool, activeTool, disabled, label, shortcut, icon, activeI
|
|
|
24
25
|
return (_jsx(Tooltip, { content: label, shortcut: shortcut, stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", size: "large", className: "canvas-toolbar__tool", label: label, "aria-pressed": active, "data-test": `canvas-tool-${tool}`, "data-active": active ? "true" : "false", disabled: disabled, onClick: () => onToolChange(tool), children: active ? activeIcon : icon }) }));
|
|
25
26
|
}
|
|
26
27
|
export function CanvasToolbar({ tool, sketchEnabled, onToolChange, }) {
|
|
27
|
-
return (_jsxs("div", { className: "canvas-toolbar", "data-test": "canvas-toolbar", role: "toolbar", "aria-label": "Canvas tools", children: [_jsxs("div", { className: "canvas-toolbar__tools", role: "group", "aria-label": "Interaction tools", children: [_jsx(ToolButton, { tool: "design", activeTool: tool, label: "Select", shortcut: "V", icon: _jsx(IconPointer2, { size: ICON_SIZE, stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), activeIcon: _jsx(DesignPointerFilledIcon, { size: ICON_SIZE, "aria-hidden": "true" }), onToolChange: onToolChange }), _jsx(ToolButton, { tool: "comment", activeTool: tool, label: "Comment", shortcut: "C", icon: _jsx(IconMessageCircle, { size: ICON_SIZE, stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), activeIcon: _jsx(IconMessageCircleFilled, { size: ICON_SIZE, "aria-hidden": "true" }), onToolChange: onToolChange }), _jsx(ToolButton, { tool: "sketch", activeTool: tool, label: "Pencil", shortcut: "P", disabled: !sketchEnabled, icon: _jsx(IconSketching, { size: ICON_SIZE, stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), activeIcon: _jsx(SketchThickIcon, { size: ICON_SIZE, "aria-hidden": "true" }), onToolChange: onToolChange })] }), _jsx("div", { className: "canvas-toolbar__divider", "aria-hidden": "true" }), _jsx("div", { className: "canvas-toolbar__tools", role: "group", "aria-label": "Preview tools", children: _jsx(ToolButton, { tool: "select", activeTool: tool, label: "Use app normally", shortcut: "Hold A", icon: _jsx(SelectPlayOutlineIcon, { size: ICON_SIZE, "aria-hidden": "true" }), activeIcon: _jsx(SelectPlayFilledIcon, { size: ICON_SIZE, "aria-hidden": "true" }), onToolChange: onToolChange }) })] }));
|
|
28
|
+
return (_jsxs("div", { className: "canvas-toolbar", "data-test": "canvas-toolbar", role: "toolbar", "aria-label": "Canvas tools", children: [_jsxs("div", { className: "canvas-toolbar__tools", role: "group", "aria-label": "Interaction tools", children: [_jsx(ToolButton, { tool: "design", activeTool: tool, label: "Select", shortcut: "V", icon: _jsx(IconPointer2, { size: ICON_SIZE, stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), activeIcon: _jsx(DesignPointerFilledIcon, { size: ICON_SIZE, "aria-hidden": "true" }), onToolChange: onToolChange }), _jsx(ToolButton, { tool: "comment", activeTool: tool, label: "Comment", shortcut: "C", icon: _jsx(IconMessageCircle, { size: ICON_SIZE, stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), activeIcon: _jsx(IconMessageCircleFilled, { size: ICON_SIZE, "aria-hidden": "true" }), onToolChange: onToolChange }), _jsx(ToolButton, { tool: "sketch", activeTool: tool, label: "Pencil", shortcut: "P", disabled: !sketchEnabled, icon: _jsx(IconSketching, { size: ICON_SIZE, stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), activeIcon: _jsx(SketchThickIcon, { size: ICON_SIZE, "aria-hidden": "true" }), onToolChange: onToolChange }), _jsx(SketchToolOptions, { open: tool === "sketch" && sketchEnabled })] }), _jsx("div", { className: "canvas-toolbar__divider", "aria-hidden": "true" }), _jsx("div", { className: "canvas-toolbar__tools", role: "group", "aria-label": "Preview tools", children: _jsx(ToolButton, { tool: "select", activeTool: tool, label: "Use app normally", shortcut: "Hold A", icon: _jsx(SelectPlayOutlineIcon, { size: ICON_SIZE, "aria-hidden": "true" }), activeIcon: _jsx(SelectPlayFilledIcon, { size: ICON_SIZE, "aria-hidden": "true" }), onToolChange: onToolChange }) })] }));
|
|
28
29
|
}
|
|
@@ -2,32 +2,35 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { getCanonicalFrameProjection } from "./projection.js";
|
|
3
3
|
import { getDraftForCard } from "../drafts/store.js";
|
|
4
4
|
import { iterationId, contentSourceUrl, getLinkedFrameIds } from "./frameContent.js";
|
|
5
|
-
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
5
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
6
6
|
import { IconCornerLeftUp } from "@tabler/icons-react";
|
|
7
7
|
import { useCanvasCards, activateIframeWorkspace, getSelectedCardId, getFocusedCardId, getCanvasCards, batchCanvasChanges, fitAllCards, focusCanvasCards, focusCard, useSelectedCardId, useFocusedCardId, resizeCard, setCardPosition, updateCardTitle, addCanvasCard, selectCard, deselectCard, CARD_GAP, updateCardUrl, } from "./canvasStore.js";
|
|
8
8
|
import { setBoardCamera, getBoardCamera, setCanvasPresentation, hasFitAllRan, useBoardCamera, useCanvasPresentation, useCanvasPresentationTransitioning, useCanvasLayoutTransitioning, useTemporaryAppInteraction, setTemporaryAppInteraction, } from "./viewStore.js";
|
|
9
9
|
import { CanvasCard } from "./CanvasCard.js";
|
|
10
|
+
import { PageCard } from "./PageCard.js";
|
|
11
|
+
import { isPageSurface } from "./pageSurface.js";
|
|
12
|
+
import { canvasFrameHost } from "./canvasFrame.js";
|
|
10
13
|
import { useCanvasLayoutAnimation } from "./useCanvasLayoutAnimation.js";
|
|
11
14
|
import { Button } from "../ui/Button.js";
|
|
12
15
|
import { useCanvasMode } from "./viewStore.js";
|
|
13
|
-
import {
|
|
16
|
+
import { getActiveDraftChanges, getChangesList, subscribeActiveDraftChanges } from "../drafts/store.js";
|
|
14
17
|
import { subscribeOriginalPreview, isOriginalPreviewActive } from "../shell/originalPreview.js";
|
|
15
18
|
import { recordCanvasStructuralProjectionReports } from "../projection/structuralProjection.js";
|
|
16
19
|
import { recordCanvasRenderedInstanceProjectionReports } from "../projection/renderedInstance.js";
|
|
17
20
|
import { recordCanvasTextProjectionReports } from "../projection/textProjection.js";
|
|
18
|
-
import {
|
|
21
|
+
import { isPreviewableChange } from "../changes/types.js";
|
|
19
22
|
import { changeKey } from "../changes/model.js";
|
|
20
|
-
import { getActiveDraftChanges } from "../changes/draftChanges.js";
|
|
21
23
|
import { beginPreviewAttempt, publishPreviewDiagnostic } from "../changes/previewDiagnostics.js";
|
|
22
24
|
import { verifyManagedStyleProjection } from "../changes/managedStyleProjection.js";
|
|
23
|
-
import { getCanvasPreviewDocument, getRegisteredFrames, isCanvasCanonicalProjectionRevisionCurrent,
|
|
25
|
+
import { getCanvasPreviewDocument, getRegisteredFrames, isCanvasCanonicalProjectionRevisionCurrent, projectToAllReadyCards, recordCanvasProjectionApplied, getFrameRegistryVersion, subscribeFrameRegistry, } from "./projection.js";
|
|
26
|
+
import { connectFrame, toViewportPoint } from "./frameConnection.js";
|
|
24
27
|
import { normalizeUrl, normalizedUrlKey } from "./normalizeUrl.js";
|
|
25
28
|
import { createNudgeUiEditorUrl } from "../../transport/editor.js";
|
|
26
29
|
import { disposeInlineTextEdit } from "../inline-text/inlineTextEditor.js";
|
|
27
|
-
import {
|
|
30
|
+
import { isProjectionAppliedMessage, isKeyboardShortcutMessage, isRenderedInstanceProjectionReportMessage, isTextProjectionReportMessage, isStructuralProjectionReportMessage, } from "./frameProtocol.js";
|
|
28
31
|
import { wheelPanDelta } from "./wheelPan.js";
|
|
29
|
-
import {
|
|
30
|
-
const canvasWorkspaceStyles = ".canvas-workspace {\n position: fixed;\n inset: 0;\n display: flex;\n flex-direction: column;\n background: var(--surface-panel);\n pointer-events: auto;\n}\n\n.canvas-workspace__board {\n flex: 1;\n overflow: hidden;\n position: relative;\n background: var(--surface-panel);\n}\n\n.canvas-workspace__focus-action {\n position: absolute;\n top: max(12px, env(safe-area-inset-top));\n left: max(12px, env(safe-area-inset-left));\n z-index: calc(var(--z-index) - 1);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--space-4);\n border-radius: var(--radius-12);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n pointer-events: auto;\n}\n\n.canvas-workspace__board.is-grabbable {\n cursor: grab;\n}\n\n.canvas-workspace__board.is-grabbing {\n cursor: grabbing;\n}\n\n/* Canvas gestures take precedence over every card interaction while Space is\n held. This also closes the short postMessage gap before an iframe receives\n its mirrored modifier state. */\n.canvas-workspace__board.is-grabbable .canvas-card,\n.canvas-workspace__board.is-grabbing .canvas-card {\n pointer-events: none;\n}\n\n.canvas-workspace__board-content {\n position: absolute;\n width: 100%;\n height: 100%;\n /*\n * Leave `will-change` unset so the browser can re-rasterize this subtree at\n * the current camera scale. A permanent `will-change: transform` promotes\n * it to a fixed bitmap, which makes iframe content look soft when zoomed.\n */\n}\n\n.canvas-workspace__board.is-grabbing .canvas-workspace__board-content {\n /* Panning benefits from a cached composited layer; zoom restores the crisp\n * default path as soon as the gesture ends. */\n will-change: transform;\n}\n\n.canvas-workspace[data-presentation=\"focus\"] .canvas-workspace__board-content {\n inset: 0;\n transform: none !important;\n}\n\n.canvas-workspace.is-presentation-transitioning .canvas-card:not(.is-presentation-card) {\n visibility: hidden !important;\n pointer-events: none !important;\n}\n\n.canvas-workspace__target-error {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n color: #6b7280;\n font: 500 14px/1.4 system-ui, sans-serif;\n}\n\n/* Moving iframes must not acquire hover state beneath a stationary pointer. */\n.canvas-workspace.is-presentation-transitioning .canvas-card__iframe,\n.canvas-workspace__board.is-layout-transitioning .canvas-card__iframe {\n pointer-events: none;\n}\n";
|
|
32
|
+
import { zoomCameraAtPointer } from "./canvasGestures.js";
|
|
33
|
+
const canvasWorkspaceStyles = ".canvas-workspace {\n position: fixed;\n inset: 0;\n display: flex;\n flex-direction: column;\n background: var(--surface-panel);\n pointer-events: auto;\n}\n\n.canvas-workspace__board {\n flex: 1;\n overflow: hidden;\n position: relative;\n background: var(--surface-panel);\n}\n\n.canvas-workspace__focus-action {\n position: absolute;\n top: max(12px, env(safe-area-inset-top));\n left: max(12px, env(safe-area-inset-left));\n z-index: calc(var(--z-index) - 1);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--space-4);\n border-radius: var(--radius-12);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n pointer-events: auto;\n}\n\n.canvas-workspace__board.is-grabbable {\n cursor: grab;\n}\n\n.canvas-workspace__board.is-grabbing {\n cursor: grabbing;\n}\n\n/* Canvas gestures take precedence over every card interaction while Space is\n held. This also closes the short postMessage gap before an iframe receives\n its mirrored modifier state. */\n.canvas-workspace__board.is-grabbable .canvas-card,\n.canvas-workspace__board.is-grabbing .canvas-card {\n pointer-events: none;\n}\n\n.canvas-workspace__board-content {\n position: absolute;\n width: 100%;\n height: 100%;\n /*\n * Leave `will-change` unset so the browser can re-rasterize this subtree at\n * the current camera scale. A permanent `will-change: transform` promotes\n * it to a fixed bitmap, which makes iframe content look soft when zoomed.\n */\n}\n\n.canvas-workspace__board.is-grabbing .canvas-workspace__board-content {\n /* Panning benefits from a cached composited layer; zoom restores the crisp\n * default path as soon as the gesture ends. */\n will-change: transform;\n}\n\n.canvas-workspace[data-presentation=\"focus\"] .canvas-workspace__board-content {\n inset: 0;\n transform: none !important;\n}\n\n.canvas-workspace.is-presentation-transitioning .canvas-card:not(.is-presentation-card) {\n visibility: hidden !important;\n pointer-events: none !important;\n}\n\n.canvas-workspace__target-error {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n color: #6b7280;\n font: 500 14px/1.4 system-ui, sans-serif;\n}\n\n/* Moving iframes must not acquire hover state beneath a stationary pointer. */\n.canvas-workspace.is-presentation-transitioning .canvas-card__iframe,\n.canvas-workspace__board.is-layout-transitioning .canvas-card__iframe {\n pointer-events: none;\n}\n\n/* The page surface edits the live page under the workspace, so only the\n toolbar takes pointer input. */\n.canvas-workspace[data-surface=\"page\"],\n.canvas-workspace[data-surface=\"page\"] .canvas-workspace__board {\n background: transparent;\n pointer-events: none;\n}\n\n.canvas-workspace[data-surface=\"page\"] .canvas-toolbar {\n pointer-events: auto;\n}\n";
|
|
31
34
|
const canvasCardStyles = ".canvas-card {\n position: relative;\n}\n\n/* Extend the card's hover region to the toolbar above the iframe. */\n.canvas-card::before {\n content: \"\";\n position: absolute;\n right: 0;\n bottom: 100%;\n left: 0;\n height: calc((var(--control-height-large) + var(--space-16)) * var(--canvas-toolbar-scale, 1));\n pointer-events: auto;\n}\n\n.canvas-card__frame {\n position: relative;\n width: 100%;\n height: 100%;\n background: var(--surface-panel);\n border-radius: 0;\n overflow: hidden;\n}\n\n.canvas-card.is-selected .canvas-card__frame {\n outline: 2px solid var(--frame-accent, var(--accent));\n outline-offset: -2px;\n}\n\n.canvas-card.is-focus-presentation .canvas-card__frame {\n outline: none;\n}\n\n.canvas-card.is-focused.is-selected:not(.is-linked) .canvas-card__frame {\n animation: canvas-card-focus 900ms ease-out;\n}\n\n@keyframes canvas-card-focus {\n 0%, 35% { outline: 3px solid var(--frame-accent, var(--accent)); outline-offset: -3px; }\n 100% { outline: 0 solid transparent; outline-offset: -2px; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .canvas-card.is-focused .canvas-card__frame {\n animation: none;\n }\n}\n\n.canvas-card__header {\n display: flex;\n align-items: center;\n position: absolute;\n left: 0;\n right: 0;\n bottom: calc(100% + var(--space-8));\n justify-content: flex-end;\n z-index: 3;\n opacity: 1;\n visibility: visible;\n pointer-events: auto;\n}\n\n.canvas-card__drag-surface {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--space-8);\n flex: 0 1 auto;\n margin-right: auto;\n min-width: 0;\n min-height: var(--control-height-large);\n padding: 0;\n cursor: grab;\n user-select: none;\n touch-action: none;\n}\n\n.canvas-card.is-dragging .canvas-card__drag-surface {\n cursor: grabbing;\n}\n\n.canvas-card__identity {\n display: flex;\n align-items: center;\n gap: var(--space-8);\n min-width: 0;\n}\n\n.canvas-card__dimensions {\n display: flex;\n align-items: center;\n gap: var(--space-8);\n position: relative;\n flex: 0 1 auto;\n min-width: 0;\n max-width: 260px;\n color: var(--text-secondary);\n font-size: var(--font-size-base);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n pointer-events: none;\n}\n\n.canvas-card__title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.canvas-card__live-badge,\n.canvas-card__iteration-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 20px;\n height: 20px;\n background: var(--accent-surface);\n color: var(--accent);\n border-radius: var(--radius-4);\n pointer-events: auto;\n}\n\n.canvas-card__floating-toolbar {\n position: absolute;\n left: 50%;\n flex-shrink: 0;\n width: max-content;\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n transform: translate(-50%, calc(-24px + var(--space-4)));\n transition: opacity 140ms ease-out, transform 140ms ease-out, visibility 140ms;\n}\n\n.canvas-card:hover .canvas-card__floating-toolbar,\n.canvas-card:focus-within .canvas-card__floating-toolbar,\n.canvas-card__floating-toolbar[data-busy=\"true\"] {\n opacity: 1;\n visibility: visible;\n pointer-events: auto;\n transform: translate(-50%, -24px);\n}\n\n.canvas-card__actions.canvas-toolbar {\n position: static;\n width: max-content;\n transform: none;\n white-space: nowrap;\n}\n\n.canvas-card__actions .canvas-toolbar__divider {\n margin-inline: 0;\n}\n\n@media (hover: none) {\n .canvas-card.is-selected .canvas-card__floating-toolbar {\n opacity: 1;\n visibility: visible;\n pointer-events: auto;\n transform: translate(-50%, -24px);\n }\n}\n\n.canvas-card.is-dragging .canvas-card__floating-toolbar,\n.canvas-workspace__board.is-grabbable .canvas-card__floating-toolbar,\n.canvas-workspace__board.is-grabbing .canvas-card__floating-toolbar,\n.canvas-workspace.is-presentation-transitioning .canvas-card__floating-toolbar,\n.canvas-workspace__board.is-layout-transitioning .canvas-card__floating-toolbar {\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .canvas-card__floating-toolbar { transition: none; }\n}\n\n.canvas-card__iteration-error {\n position: absolute;\n right: 0;\n bottom: calc(100% + var(--space-8));\n color: var(--text-danger, #b42318);\n font-size: 12px;\n white-space: nowrap;\n}\n\n.canvas-card__iframe {\n width: 100%;\n height: 100%;\n border: none;\n display: block;\n}\n\n.canvas-card__sketch-overlay {\n position: absolute;\n inset: 0;\n z-index: 2;\n display: block;\n pointer-events: none;\n}\n\n.canvas-card__loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--text-muted, #777777);\n font-size: 12px;\n font-weight: 500;\n}\n\n.canvas-card__error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n height: 100%;\n padding: 20px;\n text-align: center;\n}\n\n.canvas-card__error p {\n margin: 0;\n color: var(--text-muted, #777777);\n font-size: 12px;\n font-weight: 500;\n}\n\n.canvas-card__error-actions {\n display: flex;\n gap: 8px;\n}\n\n.canvas-card__resize-handle {\n position: absolute;\n z-index: 3;\n display: flex;\n align-items: flex-end;\n justify-content: flex-end;\n padding: 4px;\n color: var(--text-muted, #777777);\n opacity: 0;\n transition: opacity 150ms ease;\n touch-action: none;\n user-select: none;\n}\n\n.canvas-card__resize-handle--top,\n.canvas-card__resize-handle--bottom {\n right: 16px;\n left: 16px;\n height: 16px;\n cursor: ns-resize;\n}\n\n.canvas-card__resize-handle--top {\n top: 0;\n}\n\n.canvas-card__resize-handle--bottom {\n bottom: 0;\n}\n\n.canvas-card__resize-handle--left,\n.canvas-card__resize-handle--right {\n top: 16px;\n bottom: 16px;\n width: 16px;\n cursor: ew-resize;\n}\n\n.canvas-card__resize-handle--left {\n left: 0;\n}\n\n.canvas-card__resize-handle--right {\n right: 0;\n}\n\n.canvas-card__resize-handle--top-left,\n.canvas-card__resize-handle--top-right,\n.canvas-card__resize-handle--bottom-right,\n.canvas-card__resize-handle--bottom-left {\n z-index: 4;\n width: 24px;\n height: 24px;\n cursor: nwse-resize;\n}\n\n.canvas-card__resize-handle--top-left {\n top: 0;\n left: 0;\n}\n\n.canvas-card__resize-handle--top-right {\n top: 0;\n right: 0;\n cursor: nesw-resize;\n}\n\n.canvas-card__resize-handle--bottom-right {\n right: 0;\n bottom: 0;\n}\n\n.canvas-card__resize-handle--bottom-left {\n bottom: 0;\n left: 0;\n cursor: nesw-resize;\n}\n\n.canvas-card:hover .canvas-card__resize-handle,\n.canvas-card:focus-within .canvas-card__resize-handle {\n opacity: 1;\n}\n\n.canvas-card__resize-handle:hover {\n color: var(--accent);\n}\n\n.canvas-card__resize-handle:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n opacity: 1;\n}\n\n.canvas-card.is-linked {\n --frame-accent: #a855f7;\n}\n\n.canvas-card.is-linked:not(.is-focus-presentation) .canvas-card__frame {\n outline: 2px solid var(--frame-accent);\n outline-offset: -2px;\n}\n\n.canvas-card__iteration-badge {\n color: var(--text-secondary);\n background: var(--surface-raised-2x);\n}\n";
|
|
32
35
|
const foundationStyles = ":host {\n all: initial;\n --surface-panel: #F5F5F4;\n --surface-control: var(--surface-well);\n --surface-control-hover: var(--surface-well-2x);\n --surface-raised: #FAFAFA;\n --surface-raised-2x: #fff ;\n --surface-raised-hover: #F5F5F4;\n --surface-well: hsla(0, 0%, 0%, 0.05);\n --surface-well-2x: hsla(0, 0%, 0%, 0.10);\n\n --border-subtle: hsla(0, 0%, 0%, 0.1);\n --border-medium: hsla(0, 0%, 0%, 0.2);\n --border-strong: #d8d8d8;\n\n --text-primary: #242424;\n --text-secondary: #6f6f6f;\n --text-muted: #777777;\n --text-muted-subtle: #a1a1a1;\n\n --accent: #0096ff;\n --accent-surface: rgba(0, 176, 255, 0.15);\n --warning: #d97706;\n --warning-text: #9a5b00;\n --warning-surface: #fff6e5;\n --danger: #df4b4b;\n --danger-text: #bd3636;\n --danger-surface: rgba(223, 75, 75, 0.2);\n --success: #2e7d52;\n\n --space-0: 0;\n --space-4: 4px;\n --space-8: 8px;\n --space-12: 12px;\n --space-16: 16px;\n --space-20: 20px;\n --space-24: 24px;\n --space-32: 32px;\n --space-40: 40px;\n --space-48: 48px;\n\n /* Radius */\n --radius-0: 0;\n --radius-4: 4px;\n --radius-8: 8px;\n --radius-12: 12px;\n --radius-16: 16px;\n --radius-20: 20px;\n --radius-24: 24px;\n --radius-pill: 999px;\n\n --radius-control: var(--radius-8);\n --shadow-outline: 0 0 0 1px rgba(0, 0, 0, 0.05);\n --shadow-popup: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 16px rgba(0, 0, 0, 0.10);\n --shadow-control: inset 0 -2px 0 rgba(0, 0, 0, 0.03), 0 0 0 0.5px rgba(0, 0, 0, 0.25);\n --z-index: 2147483647;\n\n --font-family: \"Inter Variable\", Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-size-base: 12px;\n --font-size-small: 10px;\n --font-weight-base: 400;\n --font-weight-label: 600;\n\n /* Control semantic sizes */\n --control-height-small: 24px;\n --control-height-large: 32px;\n --control-height-xlarge: 40px;\n --label-column: 104px;\n\n --icon-size-small: 16px;\n --icon-size-large: 20px;\n --icon-stroke-width: 1.5px;\n\n position: fixed;\n inset: 0;\n z-index: var(--z-index);\n pointer-events: none;\n font-family: var(--font-family);\n background: transparent;\n box-sizing: border-box;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nbutton,\ninput,\nselect {\n font-family: inherit;\n}\n";
|
|
33
36
|
import { useInspectorOpen } from "../shell/openStore.js";
|
|
@@ -39,7 +42,8 @@ import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
|
|
|
39
42
|
import { subscribeCanvasRendererMessages } from "./rendererMessageRouter.js";
|
|
40
43
|
import { adjacentCanvasZoomLevel, CanvasToolbar, } from "./CanvasToolbar.js";
|
|
41
44
|
import { APP_INTERACTION_KEY, canvasToolForEvent, canvasToolForCode } from "./keyboardShortcuts.js";
|
|
42
|
-
const canvasToolbarStyles = ".canvas-toolbar {\n position: absolute;\n left: 50%;\n bottom: var(--space-20);\n /* Keep the persistent controls above the live sketch surface. The sketch\n toolbar gets the next layer so both toolbars remain available together. */\n z-index: calc(var(--z-index) - 2);\n display: flex;\n align-items: center;\n gap: var(--space-4);\n padding: var(--space-4);\n border-radius: var(--radius-12);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n transform: translateX(-50%);\n pointer-events: auto;\n}\n\n/* The live sketch surface is a separate stacking context. Keep a copy of the\n toolbar at the root layer while sketching so pointer events reach the\n controls instead of the full-screen SVG drawing surface. */\n.canvas-toolbar__portal {\n position: fixed;\n inset: 0;\n z-index: var(--z-index);\n pointer-events: none;\n}\n\n.canvas-toolbar__portal > .canvas-toolbar {\n pointer-events: auto;\n}\n\n.canvas-toolbar__tools {\n display: flex;\n align-items: center;\n gap: var(--space-4);\n}\n\n.canvas-toolbar__divider {\n flex-shrink: 0;\n width: 1px;\n height: var(--control-height-small);\n margin-inline: 0;\n background: var(--border-subtle);\n}\n\n.canvas-toolbar__tool[data-active=\"true\"] {\n background: var(--accent-surface);\n color: var(--accent);\n}\n";
|
|
45
|
+
const canvasToolbarStyles = ".canvas-toolbar {\n position: absolute;\n left: 50%;\n bottom: var(--space-20);\n /* Keep the persistent controls above the live sketch surface. The sketch\n toolbar gets the next layer so both toolbars remain available together. */\n z-index: calc(var(--z-index) - 2);\n display: flex;\n align-items: center;\n gap: var(--space-4);\n padding: var(--space-4);\n border-radius: var(--radius-12);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n transform: translateX(-50%);\n pointer-events: auto;\n}\n\n/* The live sketch surface is a separate stacking context. Keep a copy of the\n toolbar at the root layer while sketching so pointer events reach the\n controls instead of the full-screen SVG drawing surface. */\n.canvas-toolbar__portal {\n position: fixed;\n inset: 0;\n z-index: var(--z-index);\n pointer-events: none;\n}\n\n.canvas-toolbar__portal > .canvas-toolbar {\n pointer-events: auto;\n}\n\n.canvas-toolbar__tools {\n display: flex;\n align-items: center;\n gap: var(--space-4);\n}\n\n.canvas-toolbar__divider {\n flex-shrink: 0;\n width: 1px;\n height: var(--control-height-small);\n margin-inline: 0;\n background: var(--border-subtle);\n}\n\n.canvas-toolbar__tool[data-active=\"true\"] {\n background: var(--accent-surface);\n color: var(--accent);\n}\n\n/* Tool options slide out beside their tool. The component sets an explicit\n pixel width (0 or the content's natural width) so the toolbar grows smoothly. */\n.tool-options {\n flex-shrink: 0;\n margin-inline-start: calc(var(--space-4) * -1);\n /* Clip the slide horizontally only. Vertically the clip reaches the bar's\n padding, so illustrations that bleed to its bottom edge are never cut\n while the drawer is still animating open. */\n clip-path: inset(-64px 0 calc(var(--space-4) * -1));\n opacity: 0;\n transition:\n width 220ms cubic-bezier(0.2, 0, 0, 1),\n margin-inline-start 220ms cubic-bezier(0.2, 0, 0, 1),\n opacity 160ms ease;\n}\n\n.tool-options[data-expanded=\"true\"] {\n margin-inline-start: 0;\n opacity: 1;\n}\n\n/* Once the slide finishes, let tool content and popovers rise above the bar. */\n.tool-options[data-settled=\"true\"] {\n clip-path: none;\n}\n\n.tool-options[aria-hidden=\"true\"] {\n pointer-events: none;\n}\n\n.tool-options__inner {\n display: flex;\n align-items: center;\n gap: var(--space-4);\n width: max-content;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tool-options {\n transition: none;\n }\n}\n";
|
|
46
|
+
const sketchToolOptionsStyles = "/* Pen, eraser, color and rectangle options shown in the canvas toolbar drawer. */\n\n.sketch-options__illustrated {\n appearance: none;\n position: relative;\n flex-shrink: 0;\n width: var(--control-height-xlarge);\n height: var(--control-height-xlarge);\n padding: 0;\n border: 0;\n border-radius: var(--radius-control);\n background: transparent;\n cursor: pointer;\n /* Clip the illustration at the toolbar's bottom edge, never above it. */\n clip-path: inset(-64px calc(var(--space-4) * -1) calc(var(--space-4) * -1));\n}\n\n/* Well behind the active tool, running through the toolbar padding to its bottom edge. */\n.sketch-options__illustrated::before {\n content: \"\";\n position: absolute;\n inset: 0 0 calc(var(--space-4) * -1);\n border-radius: var(--radius-control) var(--radius-control) 0 0;\n background: var(--surface-well);\n opacity: 0;\n transition: opacity 180ms ease;\n}\n\n.tool-options[data-settled=\"true\"] .sketch-options__illustrated[data-active=\"true\"]::before {\n opacity: 1;\n}\n\n.sketch-options__illustration {\n /* Artwork below the original crop line; lets the base stay visible while raised. */\n --illustration-bleed: 0px;\n position: absolute;\n left: 50%;\n /* Extend through the toolbar padding so the base sits on its bottom edge. */\n bottom: calc(var(--space-4) * -1 - var(--illustration-bleed));\n filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.12));\n transform: translate(-50%, 0);\n transition: transform 180ms cubic-bezier(0.2, 0, 0, 1);\n}\n\n.sketch-options__illustration--pen {\n --illustration-bleed: 9px;\n}\n\n.sketch-options__illustration--eraser {\n --illustration-bleed: 11px;\n}\n\n.sketch-options__illustrated:hover .sketch-options__illustration {\n transform: translate(-50%, calc(var(--space-4) * -1));\n}\n\n.tool-options[data-settled=\"true\"] .sketch-options__illustrated[data-active=\"true\"] .sketch-options__illustration {\n transform: translate(-50%, 0);\n}\n\n.sketch-options__illustrated:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: -2px;\n}\n\n/* The rectangle shares the pen and eraser's quiet well instead of the accent active style. */\n.canvas-toolbar__tool.sketch-options__rectangle[data-active=\"true\"] {\n background: var(--surface-well);\n color: var(--text-primary);\n}\n\n.sketch-options__color {\n position: relative;\n display: flex;\n}\n\n.sketch-options__color-trigger {\n appearance: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--control-height-xlarge);\n height: var(--control-height-xlarge);\n padding: 0;\n border: 0;\n border-radius: var(--radius-control);\n background: transparent;\n color: var(--text-primary);\n cursor: pointer;\n transition: background-color 120ms ease;\n}\n\n.sketch-options__color-trigger:hover,\n.sketch-options__color-trigger[data-open=\"true\"] {\n background: var(--surface-well);\n}\n\n.sketch-options__swatch {\n display: block;\n /* Fades back in as the palette collapses onto it. */\n transition: opacity 120ms ease 150ms;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);\n}\n\n/* The hexagon frames the trigger, so its swatch steps aside while the palette is open. */\n.sketch-options__color-trigger[data-open=\"true\"] .sketch-options__swatch {\n opacity: 0;\n transition-delay: 0s;\n}\n\n/* Colors pop out as a hexagon of overlapping swatches centered on the color trigger. */\n.sketch-options__palette {\n position: absolute;\n z-index: 1;\n left: 50%;\n top: 50%;\n width: 0;\n height: 0;\n}\n\n.sketch-options__palette-option {\n appearance: none;\n position: absolute;\n left: calc(var(--swatch-size) / -2);\n top: calc(var(--swatch-size) / -2);\n width: var(--swatch-size);\n height: var(--swatch-size);\n padding: 0;\n border: 0;\n border-radius: 50%;\n cursor: pointer;\n translate: var(--swatch-x) var(--swatch-y);\n box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);\n transition:\n scale 120ms cubic-bezier(0.2, 0, 0, 1),\n box-shadow 120ms ease;\n animation: sketch-options-swatch-in 180ms cubic-bezier(0.2, 0, 0, 1) var(--swatch-delay, 0ms) backwards;\n}\n\n.sketch-options__palette-option:hover {\n scale: 1.05;\n}\n\n/* The selected swatch sits above its neighbors, separated from them by a light rim. */\n.sketch-options__palette-option[data-active=\"true\"] {\n box-shadow:\n inset 0 0 0 1px rgba(0, 0, 0, 0.08),\n 0 0 0 2px var(--surface-raised-2x),\n 0 2px 8px rgba(0, 0, 0, 0.2);\n}\n\n.sketch-options__palette-option:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n\n.sketch-options__palette[data-state=\"closing\"] {\n pointer-events: none;\n}\n\n/* Closing plays the fan-out in reverse, last swatch first. */\n.sketch-options__palette[data-state=\"closing\"] .sketch-options__palette-option {\n animation: sketch-options-swatch-out 180ms cubic-bezier(0.2, 0, 0, 1) var(--swatch-delay-out, 0ms) forwards;\n}\n\n@keyframes sketch-options-swatch-out {\n to {\n opacity: 0;\n scale: 0.4;\n translate: 0 0;\n }\n}\n\n@keyframes sketch-options-swatch-in {\n from {\n opacity: 0;\n scale: 0.4;\n translate: 0 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sketch-options__illustrated::before,\n .sketch-options__illustration {\n transition: none;\n }\n\n .sketch-options__palette-option {\n animation: none;\n transition: none;\n }\n}\n";
|
|
43
47
|
import { getActiveCanvasFrame } from "./activeCanvasDocument.js";
|
|
44
48
|
import { startSketchCapture } from "../sketch/SketchWorkspace.js";
|
|
45
49
|
import { cancelSketchInteraction, useSketchInteractionActive } from "../sketch/interaction.js";
|
|
@@ -48,7 +52,8 @@ import { createCommentCursor, DESIGN_SELECT_CURSOR, PAN_CURSOR } from "../ui/cus
|
|
|
48
52
|
import { addLinkedFrame, createIteration as createWorkspaceIteration, removeFrame } from "../workspace/commands.js";
|
|
49
53
|
import { getSelectedElements, setSelectedElement } from "../selection/selectionStore.js";
|
|
50
54
|
import { setCommentToolActive } from "../comments/store.js";
|
|
51
|
-
|
|
55
|
+
import { InspectorStyle } from "../ui/InspectorStyle.js";
|
|
56
|
+
const WORKSPACE_STYLES = [foundationStyles, canvasWorkspaceStyles, canvasCardStyles, canvasToolbarStyles, sketchToolOptionsStyles].join("\n");
|
|
52
57
|
const PRESENTATION_TRANSITION_MS = 200;
|
|
53
58
|
function snapshotRect(rect) {
|
|
54
59
|
return {
|
|
@@ -60,6 +65,7 @@ function snapshotRect(rect) {
|
|
|
60
65
|
}
|
|
61
66
|
export function CanvasWorkspace({ primaryUrl }) {
|
|
62
67
|
const mode = useCanvasMode();
|
|
68
|
+
const pageSurface = isPageSurface();
|
|
63
69
|
const runtimeConfig = useNudgeUiRuntimeConfig();
|
|
64
70
|
const inspectorSession = useInspectorSession();
|
|
65
71
|
const inspectorOpen = useInspectorOpen();
|
|
@@ -70,8 +76,10 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
70
76
|
const selectedCardId = useSelectedCardId();
|
|
71
77
|
const focusedCardId = useFocusedCardId();
|
|
72
78
|
const sketchActive = useSketchInteractionActive();
|
|
73
|
-
const sketchEnabled = isNudgeUiDev()
|
|
79
|
+
const sketchEnabled = isNudgeUiDev()
|
|
80
|
+
&& (runtimeConfig.demo !== true || isNudgeUiFeatureEnabled(runtimeConfig.features, "sketch"));
|
|
74
81
|
const canvasIterationsEnabled = isNudgeUiFeatureEnabled(runtimeConfig.features, "canvasIterations");
|
|
82
|
+
useSyncExternalStore(subscribeFrameRegistry, getFrameRegistryVersion);
|
|
75
83
|
const sketchAvailable = sketchEnabled && getActiveCanvasFrame() !== null;
|
|
76
84
|
const boardRef = useRef(null);
|
|
77
85
|
useCanvasLayoutAnimation(boardRef, cards, camera, presentation);
|
|
@@ -81,16 +89,17 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
81
89
|
const framePanContextRef = useRef(null);
|
|
82
90
|
const spaceHeldRef = useRef(false);
|
|
83
91
|
const fitAllScheduledRef = useRef(false);
|
|
84
|
-
|
|
92
|
+
// Track the consumed primaryUrl prop separately from navigation inside a frame.
|
|
93
|
+
const activatedPrimaryUrlRef = useRef(null);
|
|
85
94
|
const demoSeededRef = useRef(false);
|
|
86
95
|
const previousPresentationRef = useRef(presentation);
|
|
87
96
|
const presentationAnimationRef = useRef(null);
|
|
88
97
|
const [boardCursorClass, setBoardCursorClass] = useState("");
|
|
89
|
-
const [selectedTool, setInteractionTool] = useState("design");
|
|
98
|
+
const [selectedTool, setInteractionTool] = useState(runtimeConfig.demo === true ? "select" : "design");
|
|
90
99
|
const temporaryAppInteraction = useTemporaryAppInteraction();
|
|
91
100
|
const interactionTool = temporaryAppInteraction ? "select" : selectedTool;
|
|
92
101
|
const layoutTransitioning = useCanvasLayoutTransitioning();
|
|
93
|
-
const rendererInteractionsEnabled = interactionTool !== "select" && !layoutTransitioning;
|
|
102
|
+
const rendererInteractionsEnabled = interactionTool !== "select" && !layoutTransitioning && !sketchActive;
|
|
94
103
|
const rendererCursor = interactionTool === "comment" ? "comment" : "design";
|
|
95
104
|
const boardCursor = boardCursorClass === "is-grabbing"
|
|
96
105
|
? "grabbing"
|
|
@@ -157,9 +166,9 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
157
166
|
const board = boardRef.current;
|
|
158
167
|
if (!primaryUrl || !board)
|
|
159
168
|
return;
|
|
160
|
-
if (
|
|
169
|
+
if (activatedPrimaryUrlRef.current === primaryUrl && cards.length > 0)
|
|
161
170
|
return;
|
|
162
|
-
|
|
171
|
+
activatedPrimaryUrlRef.current = primaryUrl;
|
|
163
172
|
const viewport = {
|
|
164
173
|
width: board.clientWidth,
|
|
165
174
|
height: board.clientHeight,
|
|
@@ -219,7 +228,9 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
219
228
|
}
|
|
220
229
|
}, [primaryUrl, cards.length, presentation, runtimeConfig.demo, runtimeConfig.demoPages, runtimeConfig.demoCardLabels]);
|
|
221
230
|
useEffect(() => {
|
|
222
|
-
if (
|
|
231
|
+
if (pageSurface)
|
|
232
|
+
return;
|
|
233
|
+
if (primaryUrl && activatedPrimaryUrlRef.current !== primaryUrl)
|
|
223
234
|
return;
|
|
224
235
|
const activeCardId = selectedCardId ?? focusedCardId ?? cards[0]?.id;
|
|
225
236
|
const activeCard = cards.find((card) => card.id === activeCardId);
|
|
@@ -230,63 +241,31 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
230
241
|
window.history.replaceState(window.history.state, "", editorUrl);
|
|
231
242
|
}
|
|
232
243
|
}, [cards, focusedCardId, primaryUrl, selectedCardId]);
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
type: "pan-modifier",
|
|
236
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
237
|
-
projectId: PROJECT_ID,
|
|
238
|
-
workspaceId: WORKSPACE_ID,
|
|
239
|
-
cardId,
|
|
240
|
-
spaceHeld,
|
|
241
|
-
cursor: spaceHeld ? "drag" : rendererCursor,
|
|
242
|
-
}, window.location.origin);
|
|
243
|
-
}, [rendererCursor]);
|
|
244
|
-
const sendInspectorInteractionState = useCallback((iframe, cardId, open, interactionsEnabled, cursor) => {
|
|
245
|
-
iframe.contentWindow?.postMessage({
|
|
244
|
+
const sendInspectorInteractionState = useCallback((frame, open, interactionsEnabled, cursor) => {
|
|
245
|
+
frame.post({
|
|
246
246
|
type: "inspector-interaction-state",
|
|
247
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
248
|
-
projectId: PROJECT_ID,
|
|
249
|
-
workspaceId: WORKSPACE_ID,
|
|
250
|
-
cardId,
|
|
251
247
|
open,
|
|
252
248
|
interactionsEnabled,
|
|
253
249
|
cursor,
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
}, []);
|
|
257
|
-
const sendLinkTargetState = useCallback((iframe, cardId, openInCard) => {
|
|
258
|
-
iframe.contentWindow?.postMessage({
|
|
259
|
-
type: "link-target-state",
|
|
260
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
261
|
-
projectId: PROJECT_ID,
|
|
262
|
-
workspaceId: WORKSPACE_ID,
|
|
263
|
-
cardId,
|
|
264
|
-
openInCard,
|
|
265
|
-
}, window.location.origin);
|
|
250
|
+
...(cursor === "comment" ? { commentCursor: createCommentCursor(boardRef.current) } : {}),
|
|
251
|
+
});
|
|
266
252
|
}, []);
|
|
267
|
-
const sendBoardGestureState = useCallback((
|
|
268
|
-
|
|
269
|
-
type: "board-gesture-state",
|
|
270
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
271
|
-
projectId: PROJECT_ID,
|
|
272
|
-
workspaceId: WORKSPACE_ID,
|
|
273
|
-
cardId,
|
|
274
|
-
enabled,
|
|
275
|
-
panScroll: enabled && interactionTool !== "select",
|
|
276
|
-
}, window.location.origin);
|
|
253
|
+
const sendBoardGestureState = useCallback((frame, enabled) => {
|
|
254
|
+
frame.post({ type: "board-gesture-state", enabled, panScroll: enabled && interactionTool !== "select" });
|
|
277
255
|
}, [interactionTool]);
|
|
278
256
|
useEffect(() => {
|
|
279
257
|
for (const [cardId, iframe] of getRegisteredFrames()) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
258
|
+
const frame = connectFrame(cardId, iframe);
|
|
259
|
+
sendInspectorInteractionState(frame, inspectorOpen, rendererInteractionsEnabled, rendererCursor);
|
|
260
|
+
sendBoardGestureState(frame, presentation === "canvas");
|
|
261
|
+
frame.post({ type: "link-target-state", openInCard: linksOpenInCards });
|
|
283
262
|
}
|
|
284
|
-
}, [inspectorOpen, linksOpenInCards, presentation, rendererCursor, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState
|
|
263
|
+
}, [inspectorOpen, linksOpenInCards, presentation, rendererCursor, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState]);
|
|
285
264
|
const broadcastPanModifier = useCallback((spaceHeld) => {
|
|
286
265
|
for (const [cardId, iframe] of getRegisteredFrames()) {
|
|
287
|
-
|
|
266
|
+
connectFrame(cardId, iframe).post({ type: "pan-modifier", spaceHeld });
|
|
288
267
|
}
|
|
289
|
-
}, [
|
|
268
|
+
}, []);
|
|
290
269
|
const zoomAtPointer = useCallback((point, deltaY) => {
|
|
291
270
|
const board = boardRef.current;
|
|
292
271
|
if (!board)
|
|
@@ -347,7 +326,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
347
326
|
if (nextTool === "sketch") {
|
|
348
327
|
if (!sketchEnabled)
|
|
349
328
|
return;
|
|
350
|
-
const hostElement = getActiveCanvasFrame();
|
|
329
|
+
const hostElement = canvasFrameHost(getActiveCanvasFrame());
|
|
351
330
|
if (!hostElement)
|
|
352
331
|
return;
|
|
353
332
|
stopPanMode();
|
|
@@ -413,6 +392,8 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
413
392
|
return () => window.removeEventListener("keydown", onKeyDown, true);
|
|
414
393
|
}, [handleToolChange, mode, sketchAvailable]);
|
|
415
394
|
const handleZoomStep = useCallback((direction) => {
|
|
395
|
+
if (sketchActive)
|
|
396
|
+
return;
|
|
416
397
|
if (presentation === "focus") {
|
|
417
398
|
if (direction < 0) {
|
|
418
399
|
batchCanvasChanges(() => {
|
|
@@ -437,7 +418,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
437
418
|
if (nextZoom === null)
|
|
438
419
|
return;
|
|
439
420
|
setBoardCamera({ ...getBoardCamera(), zoom: nextZoom });
|
|
440
|
-
}, [presentation]);
|
|
421
|
+
}, [presentation, sketchActive]);
|
|
441
422
|
const showCanvas = useCallback(() => {
|
|
442
423
|
handleZoomStep(-1);
|
|
443
424
|
}, [handleZoomStep]);
|
|
@@ -476,7 +457,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
476
457
|
broadcastPanModifier(false);
|
|
477
458
|
}, [broadcastPanModifier, endPanning, presentation]);
|
|
478
459
|
useEffect(() => {
|
|
479
|
-
const stopChanges =
|
|
460
|
+
const stopChanges = subscribeActiveDraftChanges(() => {
|
|
480
461
|
projectToAllReadyCards();
|
|
481
462
|
});
|
|
482
463
|
const stopPreview = subscribeOriginalPreview(() => {
|
|
@@ -488,7 +469,8 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
488
469
|
};
|
|
489
470
|
}, []);
|
|
490
471
|
useEffect(() => {
|
|
491
|
-
return subscribeCanvasRendererMessages(({
|
|
472
|
+
return subscribeCanvasRendererMessages(({ frame, message: msg }) => {
|
|
473
|
+
const { identity } = frame;
|
|
492
474
|
if (msg.type === "renderer-hello")
|
|
493
475
|
return;
|
|
494
476
|
if (msg.type === "app-interaction-modifier") {
|
|
@@ -504,13 +486,11 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
504
486
|
}
|
|
505
487
|
return;
|
|
506
488
|
}
|
|
507
|
-
const frame = { cardId, iframe };
|
|
508
489
|
const syncActiveFrameUrl = (url) => {
|
|
509
490
|
if (getCanvasCards().some((card) => card.id === frame.cardId && iterationId(card.content)))
|
|
510
491
|
return;
|
|
511
492
|
const activeCardId = getSelectedCardId() ?? getFocusedCardId() ?? getCanvasCards()[0]?.id;
|
|
512
|
-
if (activeCardId === frame.cardId || getCanvasCards().length === 1) {
|
|
513
|
-
activatedTargetRef.current = url;
|
|
493
|
+
if (!pageSurface && (activeCardId === frame.cardId || getCanvasCards().length === 1)) {
|
|
514
494
|
window.history.replaceState(window.history.state, "", createNudgeUiEditorUrl(url));
|
|
515
495
|
}
|
|
516
496
|
updateCardUrl(frame.cardId, url);
|
|
@@ -521,7 +501,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
521
501
|
// the page without inspector changes; skip conflict verification.
|
|
522
502
|
if (isOriginalPreviewActive())
|
|
523
503
|
return;
|
|
524
|
-
const frameDocument = frame.
|
|
504
|
+
const frameDocument = frame.document();
|
|
525
505
|
const plan = getCanonicalFrameProjection(frame.cardId, msg.revision);
|
|
526
506
|
const draft = getDraftForCard(frame.cardId);
|
|
527
507
|
if (frameDocument && plan && draft && isCanvasCanonicalProjectionRevisionCurrent(frameDocument, msg.revision)) {
|
|
@@ -571,10 +551,10 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
571
551
|
syncActiveFrameUrl(msg.url);
|
|
572
552
|
// A card can finish loading after Space was pressed on the controller.
|
|
573
553
|
// Seed it with the current modifier state before its first pointer event.
|
|
574
|
-
|
|
575
|
-
sendInspectorInteractionState(frame
|
|
576
|
-
sendBoardGestureState(frame
|
|
577
|
-
|
|
554
|
+
frame.post({ type: "pan-modifier", spaceHeld: spaceHeldRef.current });
|
|
555
|
+
sendInspectorInteractionState(frame, inspectorOpen, rendererInteractionsEnabled, spaceHeldRef.current ? "drag" : rendererCursor);
|
|
556
|
+
sendBoardGestureState(frame, presentation === "canvas");
|
|
557
|
+
frame.post({ type: "link-target-state", openInCard: linksOpenInCards });
|
|
578
558
|
}
|
|
579
559
|
if (msg.type === "pan-modifier") {
|
|
580
560
|
if (presentation !== "canvas")
|
|
@@ -587,34 +567,25 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
587
567
|
return;
|
|
588
568
|
}
|
|
589
569
|
if (msg.type === "wheel-pan") {
|
|
590
|
-
if (presentation === "canvas" && interactionTool !== "select")
|
|
570
|
+
if (presentation === "canvas" && !sketchActive && interactionTool !== "select")
|
|
591
571
|
panCanvasByWheel(msg.deltaX, msg.deltaY);
|
|
592
572
|
return;
|
|
593
573
|
}
|
|
594
574
|
if (msg.type === "zoom") {
|
|
595
|
-
if (presentation !== "canvas")
|
|
575
|
+
if (presentation !== "canvas" || sketchActive)
|
|
596
576
|
return;
|
|
597
|
-
|
|
598
|
-
const point = iframePointToClientPoint(iframeRect, msg.point, getBoardCamera().zoom);
|
|
599
|
-
zoomAtPointer(point, msg.deltaY);
|
|
577
|
+
zoomAtPointer(toViewportPoint(frame.viewport(), msg.point), msg.deltaY);
|
|
600
578
|
return;
|
|
601
579
|
}
|
|
580
|
+
// The pan moves the frame, so every pan point maps through the viewport captured at pan start.
|
|
602
581
|
const pointInBoard = (point) => {
|
|
603
582
|
const context = framePanContextRef.current;
|
|
604
|
-
|
|
605
|
-
return null;
|
|
606
|
-
return {
|
|
607
|
-
x: context.left + point.x * context.zoom,
|
|
608
|
-
y: context.top + point.y * context.zoom,
|
|
609
|
-
};
|
|
583
|
+
return context ? toViewportPoint(context, point) : null;
|
|
610
584
|
};
|
|
611
585
|
if (msg.type === "pan-start") {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
top: iframeRect.top,
|
|
616
|
-
zoom: getBoardCamera().zoom,
|
|
617
|
-
};
|
|
586
|
+
if (sketchActive)
|
|
587
|
+
return;
|
|
588
|
+
framePanContextRef.current = frame.viewport();
|
|
618
589
|
const point = pointInBoard(msg.point);
|
|
619
590
|
if (point)
|
|
620
591
|
startPanning(point);
|
|
@@ -632,7 +603,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
632
603
|
return;
|
|
633
604
|
}
|
|
634
605
|
if (msg.type === "frame-metadata") {
|
|
635
|
-
const frameDocument = frame.
|
|
606
|
+
const frameDocument = frame.document();
|
|
636
607
|
if (frameDocument)
|
|
637
608
|
disposeInlineTextEdit("route-disposed", frameDocument);
|
|
638
609
|
if (msg.url)
|
|
@@ -663,11 +634,11 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
663
634
|
activateIframeWorkspace(msg.url, { width: boardRef.current?.clientWidth ?? window.innerWidth, height: boardRef.current?.clientHeight ?? window.innerHeight }, { replaceActiveCard: true });
|
|
664
635
|
return;
|
|
665
636
|
}
|
|
666
|
-
const frameDocument = frame.
|
|
637
|
+
const frameDocument = frame.document();
|
|
667
638
|
if (frameDocument)
|
|
668
639
|
disposeInlineTextEdit("route-disposed", frameDocument);
|
|
669
640
|
});
|
|
670
|
-
}, [broadcastPanModifier, endPanning, handleToolChange, interactionTool, mode, panCanvasByWheel, inspectorOpen, linksOpenInCards, movePanning, presentation, rendererCursor, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState,
|
|
641
|
+
}, [broadcastPanModifier, endPanning, handleToolChange, interactionTool, mode, panCanvasByWheel, inspectorOpen, linksOpenInCards, movePanning, presentation, rendererCursor, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState, sketchActive, sketchAvailable, startPanning, zoomAtPointer]);
|
|
671
642
|
useEffect(() => {
|
|
672
643
|
if (mode === "canvas" && !hasFitAllRan()) {
|
|
673
644
|
if (!fitAllScheduledRef.current) {
|
|
@@ -758,8 +729,13 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
758
729
|
function handleGlobalWheel(e) {
|
|
759
730
|
const zoom = e.ctrlKey || e.metaKey;
|
|
760
731
|
const board = boardRef.current;
|
|
761
|
-
if (
|
|
732
|
+
if (sketchActive) {
|
|
733
|
+
if (e.composedPath().some((node) => node instanceof Element && node.closest(".sketch__prompt-panel, .sketch__annotation-editor, .panel")))
|
|
734
|
+
return;
|
|
735
|
+
e.preventDefault();
|
|
736
|
+
e.stopPropagation();
|
|
762
737
|
return;
|
|
738
|
+
}
|
|
763
739
|
const path = e.composedPath();
|
|
764
740
|
// Comment pins overlay the board without being its DOM descendants.
|
|
765
741
|
const overCommentPin = path.some((node) => node instanceof Element && node.matches(".comment-pin"));
|
|
@@ -777,10 +753,11 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
777
753
|
window.addEventListener("wheel", handleGlobalWheel, { capture: true, passive: false });
|
|
778
754
|
return () => window.removeEventListener("wheel", handleGlobalWheel, { capture: true });
|
|
779
755
|
}, [mode, presentation, sketchActive, interactionTool, panCanvasByWheel, zoomAtPointer]);
|
|
780
|
-
|
|
756
|
+
// Every page card would share the one page renderer, so restored extra cards stay unmounted.
|
|
757
|
+
const renderCard = (card) => pageSurface ? (card.id === presentationCardId ? _jsx(PageCard, { card: card, documentOwner: inspectorSession }, card.id) : null) : (_jsx(CanvasCard, { card: card, presentation: presentation, presentationCard: card.id === presentationCardId, linkedToActive: linkedFrameIds.includes(card.id), onShowFocus: showFocus, onDuplicate: duplicateDesign, onCreateIteration: canvasIterationsEnabled ? createIteration : undefined, documentOwner: inspectorSession }, card.id));
|
|
781
758
|
if (mode !== "canvas")
|
|
782
759
|
return null;
|
|
783
|
-
return (_jsxs(_Fragment, { children: [_jsx(
|
|
760
|
+
return (_jsxs(_Fragment, { children: [_jsx(InspectorStyle, { testId: "canvas-styles", css: WORKSPACE_STYLES }), _jsx("div", { className: `canvas-workspace${presentationTransitioning ? " is-presentation-transitioning" : ""}`, "data-test": "canvas-workspace", "data-presentation": presentation, "data-surface": pageSurface ? "page" : undefined, style: { right: inspectorOpen ? "min(320px, 100vw)" : 0 }, children: _jsxs("div", { className: `canvas-workspace__board ${boardCursorClass}${layoutTransitioning ? " is-layout-transitioning" : ""}`.trim(), "data-test": "canvas-board", ref: boardRef, style: { cursor: boardCursor }, onPointerDown: handleBoardPointerDown, onClick: handleBoardClick, children: [!primaryUrl ? (_jsx("div", { className: "canvas-workspace__target-error", "data-test": "canvas-target-error", children: "This editor URL does not identify an application page." })) : null, inspectorOpen && presentation === "focus" && !pageSurface ? (_jsx("div", { className: "canvas-workspace__focus-action", "data-test": "canvas-show-canvas", children: _jsxs(Button, { variant: "quiet", size: "default", type: "button", onClick: showCanvas, children: [_jsx(IconCornerLeftUp, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), "Canvas"] }) })) : null, _jsx("div", { className: "canvas-workspace__board-content", style: {
|
|
784
761
|
transform: `translate(${camera.x}px, ${camera.y}px) scale(${camera.zoom})`,
|
|
785
762
|
transformOrigin: "0 0",
|
|
786
763
|
}, "data-test": "canvas-board-content", children: cards.map(renderCard) }), inspectorOpen && !sketchActive ? (_jsx(CanvasToolbar, { tool: interactionTool, sketchEnabled: sketchAvailable, onToolChange: handleToolChange })) : null] }) }), inspectorOpen && sketchActive ? (_jsx("div", { className: "canvas-toolbar__portal", "data-test": "canvas-toolbar-portal", style: { right: inspectorOpen ? "min(320px, 100vw)" : 0 }, children: _jsx(CanvasToolbar, { tool: interactionTool, sketchEnabled: sketchAvailable, onToolChange: handleToolChange }) })) : null] }));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
2
|
interface LockedWorkspaceNoticeProps {
|
|
3
|
-
onTakeover: () => void
|
|
3
|
+
onTakeover: () => void | Promise<void>;
|
|
4
|
+
storageError?: boolean;
|
|
4
5
|
}
|
|
5
|
-
export declare function LockedWorkspaceNotice({ onTakeover }: LockedWorkspaceNoticeProps): ReactElement;
|
|
6
|
+
export declare function LockedWorkspaceNotice({ onTakeover, storageError }: LockedWorkspaceNoticeProps): ReactElement;
|
|
6
7
|
export {};
|
|
@@ -4,14 +4,18 @@ import { requestTakeover } from "./workspaceLease.js";
|
|
|
4
4
|
import { Button } from "../ui/Button.js";
|
|
5
5
|
import { UI_STYLES } from "../ui/styles.js";
|
|
6
6
|
const lockedWorkspaceStyles = ".locked-notice {\n position: fixed;\n top: max(12px, env(safe-area-inset-top));\n right: max(12px, env(safe-area-inset-right));\n width: min(320px, calc(100vw - 24px));\n padding: 18px;\n border-radius: 12px;\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n color: var(--text-primary);\n font-family: var(--font-family);\n pointer-events: auto;\n}\n\n.locked-notice__message {\n margin-bottom: 14px;\n font-size: 14px;\n font-weight: 650;\n}\n";
|
|
7
|
-
|
|
7
|
+
import { InspectorStyle } from "../ui/InspectorStyle.js";
|
|
8
|
+
export function LockedWorkspaceNotice({ onTakeover, storageError = false }) {
|
|
8
9
|
const [takingOver, setTakingOver] = useState(false);
|
|
9
|
-
function handleTakeover() {
|
|
10
|
+
async function handleTakeover() {
|
|
10
11
|
setTakingOver(true);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
try {
|
|
13
|
+
if (requestTakeover())
|
|
14
|
+
await onTakeover();
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
14
17
|
setTakingOver(false);
|
|
18
|
+
}
|
|
15
19
|
}
|
|
16
|
-
return (_jsxs(_Fragment, { children: [_jsx(
|
|
20
|
+
return (_jsxs(_Fragment, { children: [_jsx(InspectorStyle, { testId: "locked-workspace-styles", css: `${UI_STYLES}\n${lockedWorkspaceStyles}` }), _jsxs("aside", { className: "locked-notice", "data-test": "locked-workspace-notice", "aria-live": "polite", children: [_jsx("div", { className: "locked-notice__message", children: storageError ? "Can’t load saved changes. Try again." : "Nudge UI is open in another tab" }), _jsx(Button, { "data-test": "takeover-here", disabled: takingOver, onClick: handleTakeover, children: takingOver ? "Taking over..." : "Take Over Here" })] })] }));
|
|
17
21
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import type { CanvasCard } from "./canvasStore.js";
|
|
3
|
+
import type { InspectorSession } from "../session/sessionFactory.js";
|
|
4
|
+
interface PageCardProps {
|
|
5
|
+
card: CanvasCard;
|
|
6
|
+
documentOwner?: InspectorSession;
|
|
7
|
+
}
|
|
8
|
+
/** The page-surface Canvas card: the inspected page is the renderer. */
|
|
9
|
+
export declare function PageCard({ card, documentOwner }: PageCardProps): ReactElement;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { connectFrame } from "./frameConnection.js";
|
|
4
|
+
import { contentSourceUrl } from "./frameContent.js";
|
|
5
|
+
import { getPageFrame } from "./pageSurface.js";
|
|
6
|
+
import { registerCardFrame, registerCardFrameSource, sendProjectionToCard, unregisterCardFrame } from "./projection.js";
|
|
7
|
+
import { subscribeCanvasRendererMessages } from "./rendererMessageRouter.js";
|
|
8
|
+
import { disposeBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
|
|
9
|
+
import { releaseDocumentProjection } from "../projection/structuralProjection.js";
|
|
10
|
+
import { disposeRuntimeIdentityClaims } from "../runtime/runtimeIdentityClaims.js";
|
|
11
|
+
import { disposeInlineTextEdit } from "../inline-text/inlineTextEditor.js";
|
|
12
|
+
import { startClipboardHandoffController } from "../prompt/clipboardHandoff.js";
|
|
13
|
+
import { SketchFrameOverlay } from "../sketch/SketchFrameOverlay.js";
|
|
14
|
+
/** The page-surface Canvas card: the inspected page is the renderer. */
|
|
15
|
+
export function PageCard({ card, documentOwner }) {
|
|
16
|
+
const cardRef = useRef(card);
|
|
17
|
+
cardRef.current = card;
|
|
18
|
+
const [ready, setReady] = useState(false);
|
|
19
|
+
const frame = getPageFrame();
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
registerCardFrameSource(card.id, frame);
|
|
22
|
+
const session = documentOwner?.createDocumentSession(document);
|
|
23
|
+
session?.registerCleanup(() => disposeBrowserCssInspection(document));
|
|
24
|
+
session?.registerCleanup(() => releaseDocumentProjection(document));
|
|
25
|
+
session?.registerCleanup(() => disposeRuntimeIdentityClaims(document));
|
|
26
|
+
session?.registerCleanup(() => disposeInlineTextEdit("frame-disposed", document));
|
|
27
|
+
session?.registerCleanup(startClipboardHandoffController(document));
|
|
28
|
+
const announce = () => connectFrame(card.id, frame).post({ type: "parent-ready" });
|
|
29
|
+
const unsubscribe = subscribeCanvasRendererMessages(({ frame: connection, message }) => {
|
|
30
|
+
if (connection.cardId !== card.id)
|
|
31
|
+
return;
|
|
32
|
+
if (message.type === "renderer-hello") {
|
|
33
|
+
announce();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (message.type === "frame-ready") {
|
|
37
|
+
registerCardFrame(card.id, frame);
|
|
38
|
+
sendProjectionToCard(cardRef.current, frame);
|
|
39
|
+
setReady(true);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
announce();
|
|
43
|
+
return () => {
|
|
44
|
+
unsubscribe();
|
|
45
|
+
session?.dispose();
|
|
46
|
+
unregisterCardFrame(card.id);
|
|
47
|
+
setReady(false);
|
|
48
|
+
};
|
|
49
|
+
}, [card.id, documentOwner, frame]);
|
|
50
|
+
return (_jsx(SketchFrameOverlay, { cardId: card.id, iframe: frame, cardUrl: contentSourceUrl(card.content), ready: ready }));
|
|
51
|
+
}
|