nudge-ui 0.2.3 → 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 +870 -212
- 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 +5 -7
- package/dist/inspector/agent/verification.js +67 -20
- 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 +23 -12
- package/dist/inspector/canvas/CanvasElementOverlay.js +97 -128
- package/dist/inspector/canvas/CanvasToolbar.js +2 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +119 -162
- 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/frameScrollbarStyles.d.ts +3 -0
- package/dist/inspector/canvas/frameScrollbarStyles.js +21 -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 +2 -0
- package/dist/inspector/changes/codecs.js +27 -3
- package/dist/inspector/changes/draftPreview.d.ts +12 -0
- package/dist/inspector/changes/draftPreview.js +133 -0
- package/dist/inspector/changes/editModel.d.ts +18 -0
- package/dist/inspector/changes/editModel.js +16 -3
- package/dist/inspector/changes/managedStyleProjection.js +2 -1
- package/dist/inspector/changes/previewDiagnostics.js +1 -1
- package/dist/inspector/changes/types.d.ts +18 -1
- package/dist/inspector/comments/CommentsOverlay.js +18 -16
- package/dist/inspector/comments/element.js +10 -3
- 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/adapterRegistry.d.ts +6 -1
- package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
- package/dist/inspector/componentSemantics/changeAction.js +1 -1
- package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
- package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
- package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
- package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
- package/dist/inspector/componentSemantics/textBinding.js +4 -45
- package/dist/inspector/componentSemantics/types.d.ts +11 -0
- 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/capture.js +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.d.ts +15 -2
- package/dist/inspector/projection/renderedInstance.js +60 -7
- package/dist/inspector/projection/structuralProjection.d.ts +0 -9
- package/dist/inspector/projection/structuralProjection.js +16 -46
- package/dist/inspector/projection/textProjection.d.ts +8 -2
- package/dist/inspector/projection/textProjection.js +77 -24
- package/dist/inspector/prompt/clipboardHandoff.d.ts +14 -0
- package/dist/inspector/prompt/clipboardHandoff.js +47 -16
- package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
- package/dist/inspector/prompt/generatePrompt.js +248 -148
- 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/selection/sourceSite.d.ts +12 -0
- package/dist/inspector/selection/sourceSite.js +28 -0
- 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 +74 -26
- 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 +3 -2
- 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 +40 -7
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/editActions.d.ts +3 -3
- package/dist/inspector/tokens/editActions.js +15 -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 +16 -0
- package/dist/inspector/workspace/sentItems.js +33 -0
- 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 +13 -3
- 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 -47
- package/dist/inspector/changes/changesLog.js +0 -247
- package/dist/inspector/changes/draftChanges.d.ts +0 -56
- package/dist/inspector/changes/draftChanges.js +0 -300
- 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
|
@@ -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
|
|
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,17 +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
|
-
const
|
|
88
|
-
const canvasEntryAnimationRef = useRef(null);
|
|
96
|
+
const presentationAnimationRef = useRef(null);
|
|
89
97
|
const [boardCursorClass, setBoardCursorClass] = useState("");
|
|
90
|
-
const [selectedTool, setInteractionTool] = useState("design");
|
|
98
|
+
const [selectedTool, setInteractionTool] = useState(runtimeConfig.demo === true ? "select" : "design");
|
|
91
99
|
const temporaryAppInteraction = useTemporaryAppInteraction();
|
|
92
100
|
const interactionTool = temporaryAppInteraction ? "select" : selectedTool;
|
|
93
101
|
const layoutTransitioning = useCanvasLayoutTransitioning();
|
|
94
|
-
const rendererInteractionsEnabled = interactionTool !== "select" && !layoutTransitioning;
|
|
102
|
+
const rendererInteractionsEnabled = interactionTool !== "select" && !layoutTransitioning && !sketchActive;
|
|
95
103
|
const rendererCursor = interactionTool === "comment" ? "comment" : "design";
|
|
96
104
|
const boardCursor = boardCursorClass === "is-grabbing"
|
|
97
105
|
? "grabbing"
|
|
@@ -106,80 +114,61 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
106
114
|
const presentationCardId = selectedCardId ?? focusedCardId ?? cards[0]?.id ?? null;
|
|
107
115
|
const linkedFrameIds = getLinkedFrameIds(cards, selectedCardId);
|
|
108
116
|
useLayoutEffect(() => {
|
|
117
|
+
const previousPresentation = previousPresentationRef.current;
|
|
118
|
+
previousPresentationRef.current = presentation;
|
|
119
|
+
if (previousPresentation === presentation)
|
|
120
|
+
return;
|
|
121
|
+
presentationAnimationRef.current?.cancel();
|
|
122
|
+
presentationAnimationRef.current = null;
|
|
123
|
+
if (!presentationTransitioning)
|
|
124
|
+
return;
|
|
109
125
|
const board = boardRef.current;
|
|
126
|
+
const card = cards.find((candidate) => candidate.id === presentationCardId);
|
|
110
127
|
const cardElement = presentationCardId
|
|
111
128
|
? board?.querySelector(`[data-card-id="${presentationCardId}"]`)
|
|
112
129
|
: null;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
transformOrigin: "center center",
|
|
149
|
-
transform: `translate(${localTranslateX}px, ${localTranslateY}px) scale(${scaleX}, ${scaleY})`,
|
|
150
|
-
},
|
|
151
|
-
{ transformOrigin: "center center", transform: "none" },
|
|
152
|
-
], { duration: PRESENTATION_TRANSITION_MS, easing: "ease", fill: "both" });
|
|
153
|
-
canvasEntryAnimationRef.current = animation;
|
|
154
|
-
void animation.finished.then(() => {
|
|
155
|
-
if (canvasEntryAnimationRef.current !== animation)
|
|
156
|
-
return;
|
|
157
|
-
canvasEntryAnimationRef.current = null;
|
|
158
|
-
animation.cancel();
|
|
159
|
-
boardContent.style.transition = "";
|
|
160
|
-
cardElement.style.transition = "";
|
|
161
|
-
}).catch(() => {
|
|
162
|
-
if (canvasEntryAnimationRef.current !== animation)
|
|
163
|
-
return;
|
|
164
|
-
canvasEntryAnimationRef.current = null;
|
|
165
|
-
boardContent.style.transition = "";
|
|
166
|
-
cardElement.style.transition = "";
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
boardContent.style.transition = "";
|
|
171
|
-
cardElement.style.transition = "";
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
previousPresentationRef.current = presentation;
|
|
175
|
-
}, [camera.zoom, presentation, presentationCardId, presentationTransitioning]);
|
|
130
|
+
if (!board || !card || !cardElement || typeof cardElement.animate !== "function" || camera.zoom <= 0)
|
|
131
|
+
return;
|
|
132
|
+
// The previous layout is already gone, so derive it: focus fills the
|
|
133
|
+
// board, and canvas places the card through the camera.
|
|
134
|
+
const boardRect = snapshotRect(board.getBoundingClientRect());
|
|
135
|
+
const canvasRect = {
|
|
136
|
+
left: boardRect.left + camera.x + card.x * camera.zoom,
|
|
137
|
+
top: boardRect.top + camera.y + card.y * camera.zoom,
|
|
138
|
+
width: card.width * camera.zoom,
|
|
139
|
+
height: card.height * camera.zoom,
|
|
140
|
+
};
|
|
141
|
+
const oldRect = presentation === "focus" ? canvasRect : boardRect;
|
|
142
|
+
const localScale = presentation === "focus" ? 1 : camera.zoom;
|
|
143
|
+
const newRect = snapshotRect(cardElement.getBoundingClientRect());
|
|
144
|
+
if (newRect.width <= 0 || newRect.height <= 0)
|
|
145
|
+
return;
|
|
146
|
+
const localTranslateX = (oldRect.left + oldRect.width / 2 - (newRect.left + newRect.width / 2)) / localScale;
|
|
147
|
+
const localTranslateY = (oldRect.top + oldRect.height / 2 - (newRect.top + newRect.height / 2)) / localScale;
|
|
148
|
+
const scaleX = oldRect.width / newRect.width;
|
|
149
|
+
const scaleY = oldRect.height / newRect.height;
|
|
150
|
+
const animation = cardElement.animate([
|
|
151
|
+
{
|
|
152
|
+
transformOrigin: "center center",
|
|
153
|
+
transform: `translate(${localTranslateX}px, ${localTranslateY}px) scale(${scaleX}, ${scaleY})`,
|
|
154
|
+
},
|
|
155
|
+
{ transformOrigin: "center center", transform: "none" },
|
|
156
|
+
], { duration: PRESENTATION_TRANSITION_MS, easing: "ease", fill: "both" });
|
|
157
|
+
presentationAnimationRef.current = animation;
|
|
158
|
+
void animation.finished.then(() => {
|
|
159
|
+
if (presentationAnimationRef.current !== animation)
|
|
160
|
+
return;
|
|
161
|
+
presentationAnimationRef.current = null;
|
|
162
|
+
animation.cancel();
|
|
163
|
+
}).catch(() => undefined);
|
|
164
|
+
}, [camera, cards, presentation, presentationCardId, presentationTransitioning]);
|
|
176
165
|
useLayoutEffect(() => {
|
|
177
166
|
const board = boardRef.current;
|
|
178
167
|
if (!primaryUrl || !board)
|
|
179
168
|
return;
|
|
180
|
-
if (
|
|
169
|
+
if (activatedPrimaryUrlRef.current === primaryUrl && cards.length > 0)
|
|
181
170
|
return;
|
|
182
|
-
|
|
171
|
+
activatedPrimaryUrlRef.current = primaryUrl;
|
|
183
172
|
const viewport = {
|
|
184
173
|
width: board.clientWidth,
|
|
185
174
|
height: board.clientHeight,
|
|
@@ -239,7 +228,9 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
239
228
|
}
|
|
240
229
|
}, [primaryUrl, cards.length, presentation, runtimeConfig.demo, runtimeConfig.demoPages, runtimeConfig.demoCardLabels]);
|
|
241
230
|
useEffect(() => {
|
|
242
|
-
if (
|
|
231
|
+
if (pageSurface)
|
|
232
|
+
return;
|
|
233
|
+
if (primaryUrl && activatedPrimaryUrlRef.current !== primaryUrl)
|
|
243
234
|
return;
|
|
244
235
|
const activeCardId = selectedCardId ?? focusedCardId ?? cards[0]?.id;
|
|
245
236
|
const activeCard = cards.find((card) => card.id === activeCardId);
|
|
@@ -250,63 +241,31 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
250
241
|
window.history.replaceState(window.history.state, "", editorUrl);
|
|
251
242
|
}
|
|
252
243
|
}, [cards, focusedCardId, primaryUrl, selectedCardId]);
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
type: "pan-modifier",
|
|
256
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
257
|
-
projectId: PROJECT_ID,
|
|
258
|
-
workspaceId: WORKSPACE_ID,
|
|
259
|
-
cardId,
|
|
260
|
-
spaceHeld,
|
|
261
|
-
cursor: spaceHeld ? "drag" : rendererCursor,
|
|
262
|
-
}, window.location.origin);
|
|
263
|
-
}, [rendererCursor]);
|
|
264
|
-
const sendInspectorInteractionState = useCallback((iframe, cardId, open, interactionsEnabled, cursor) => {
|
|
265
|
-
iframe.contentWindow?.postMessage({
|
|
244
|
+
const sendInspectorInteractionState = useCallback((frame, open, interactionsEnabled, cursor) => {
|
|
245
|
+
frame.post({
|
|
266
246
|
type: "inspector-interaction-state",
|
|
267
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
268
|
-
projectId: PROJECT_ID,
|
|
269
|
-
workspaceId: WORKSPACE_ID,
|
|
270
|
-
cardId,
|
|
271
247
|
open,
|
|
272
248
|
interactionsEnabled,
|
|
273
249
|
cursor,
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
}, []);
|
|
277
|
-
const sendLinkTargetState = useCallback((iframe, cardId, openInCard) => {
|
|
278
|
-
iframe.contentWindow?.postMessage({
|
|
279
|
-
type: "link-target-state",
|
|
280
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
281
|
-
projectId: PROJECT_ID,
|
|
282
|
-
workspaceId: WORKSPACE_ID,
|
|
283
|
-
cardId,
|
|
284
|
-
openInCard,
|
|
285
|
-
}, window.location.origin);
|
|
250
|
+
...(cursor === "comment" ? { commentCursor: createCommentCursor(boardRef.current) } : {}),
|
|
251
|
+
});
|
|
286
252
|
}, []);
|
|
287
|
-
const sendBoardGestureState = useCallback((
|
|
288
|
-
|
|
289
|
-
type: "board-gesture-state",
|
|
290
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
291
|
-
projectId: PROJECT_ID,
|
|
292
|
-
workspaceId: WORKSPACE_ID,
|
|
293
|
-
cardId,
|
|
294
|
-
enabled,
|
|
295
|
-
panScroll: enabled && interactionTool !== "select",
|
|
296
|
-
}, window.location.origin);
|
|
253
|
+
const sendBoardGestureState = useCallback((frame, enabled) => {
|
|
254
|
+
frame.post({ type: "board-gesture-state", enabled, panScroll: enabled && interactionTool !== "select" });
|
|
297
255
|
}, [interactionTool]);
|
|
298
256
|
useEffect(() => {
|
|
299
257
|
for (const [cardId, iframe] of getRegisteredFrames()) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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 });
|
|
303
262
|
}
|
|
304
|
-
}, [inspectorOpen, linksOpenInCards, presentation, rendererCursor, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState
|
|
263
|
+
}, [inspectorOpen, linksOpenInCards, presentation, rendererCursor, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState]);
|
|
305
264
|
const broadcastPanModifier = useCallback((spaceHeld) => {
|
|
306
265
|
for (const [cardId, iframe] of getRegisteredFrames()) {
|
|
307
|
-
|
|
266
|
+
connectFrame(cardId, iframe).post({ type: "pan-modifier", spaceHeld });
|
|
308
267
|
}
|
|
309
|
-
}, [
|
|
268
|
+
}, []);
|
|
310
269
|
const zoomAtPointer = useCallback((point, deltaY) => {
|
|
311
270
|
const board = boardRef.current;
|
|
312
271
|
if (!board)
|
|
@@ -367,7 +326,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
367
326
|
if (nextTool === "sketch") {
|
|
368
327
|
if (!sketchEnabled)
|
|
369
328
|
return;
|
|
370
|
-
const hostElement = getActiveCanvasFrame();
|
|
329
|
+
const hostElement = canvasFrameHost(getActiveCanvasFrame());
|
|
371
330
|
if (!hostElement)
|
|
372
331
|
return;
|
|
373
332
|
stopPanMode();
|
|
@@ -433,6 +392,8 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
433
392
|
return () => window.removeEventListener("keydown", onKeyDown, true);
|
|
434
393
|
}, [handleToolChange, mode, sketchAvailable]);
|
|
435
394
|
const handleZoomStep = useCallback((direction) => {
|
|
395
|
+
if (sketchActive)
|
|
396
|
+
return;
|
|
436
397
|
if (presentation === "focus") {
|
|
437
398
|
if (direction < 0) {
|
|
438
399
|
batchCanvasChanges(() => {
|
|
@@ -457,7 +418,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
457
418
|
if (nextZoom === null)
|
|
458
419
|
return;
|
|
459
420
|
setBoardCamera({ ...getBoardCamera(), zoom: nextZoom });
|
|
460
|
-
}, [presentation]);
|
|
421
|
+
}, [presentation, sketchActive]);
|
|
461
422
|
const showCanvas = useCallback(() => {
|
|
462
423
|
handleZoomStep(-1);
|
|
463
424
|
}, [handleZoomStep]);
|
|
@@ -496,7 +457,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
496
457
|
broadcastPanModifier(false);
|
|
497
458
|
}, [broadcastPanModifier, endPanning, presentation]);
|
|
498
459
|
useEffect(() => {
|
|
499
|
-
const stopChanges =
|
|
460
|
+
const stopChanges = subscribeActiveDraftChanges(() => {
|
|
500
461
|
projectToAllReadyCards();
|
|
501
462
|
});
|
|
502
463
|
const stopPreview = subscribeOriginalPreview(() => {
|
|
@@ -508,7 +469,8 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
508
469
|
};
|
|
509
470
|
}, []);
|
|
510
471
|
useEffect(() => {
|
|
511
|
-
return subscribeCanvasRendererMessages(({
|
|
472
|
+
return subscribeCanvasRendererMessages(({ frame, message: msg }) => {
|
|
473
|
+
const { identity } = frame;
|
|
512
474
|
if (msg.type === "renderer-hello")
|
|
513
475
|
return;
|
|
514
476
|
if (msg.type === "app-interaction-modifier") {
|
|
@@ -524,13 +486,11 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
524
486
|
}
|
|
525
487
|
return;
|
|
526
488
|
}
|
|
527
|
-
const frame = { cardId, iframe };
|
|
528
489
|
const syncActiveFrameUrl = (url) => {
|
|
529
490
|
if (getCanvasCards().some((card) => card.id === frame.cardId && iterationId(card.content)))
|
|
530
491
|
return;
|
|
531
492
|
const activeCardId = getSelectedCardId() ?? getFocusedCardId() ?? getCanvasCards()[0]?.id;
|
|
532
|
-
if (activeCardId === frame.cardId || getCanvasCards().length === 1) {
|
|
533
|
-
activatedTargetRef.current = url;
|
|
493
|
+
if (!pageSurface && (activeCardId === frame.cardId || getCanvasCards().length === 1)) {
|
|
534
494
|
window.history.replaceState(window.history.state, "", createNudgeUiEditorUrl(url));
|
|
535
495
|
}
|
|
536
496
|
updateCardUrl(frame.cardId, url);
|
|
@@ -541,7 +501,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
541
501
|
// the page without inspector changes; skip conflict verification.
|
|
542
502
|
if (isOriginalPreviewActive())
|
|
543
503
|
return;
|
|
544
|
-
const frameDocument = frame.
|
|
504
|
+
const frameDocument = frame.document();
|
|
545
505
|
const plan = getCanonicalFrameProjection(frame.cardId, msg.revision);
|
|
546
506
|
const draft = getDraftForCard(frame.cardId);
|
|
547
507
|
if (frameDocument && plan && draft && isCanvasCanonicalProjectionRevisionCurrent(frameDocument, msg.revision)) {
|
|
@@ -591,10 +551,10 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
591
551
|
syncActiveFrameUrl(msg.url);
|
|
592
552
|
// A card can finish loading after Space was pressed on the controller.
|
|
593
553
|
// Seed it with the current modifier state before its first pointer event.
|
|
594
|
-
|
|
595
|
-
sendInspectorInteractionState(frame
|
|
596
|
-
sendBoardGestureState(frame
|
|
597
|
-
|
|
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 });
|
|
598
558
|
}
|
|
599
559
|
if (msg.type === "pan-modifier") {
|
|
600
560
|
if (presentation !== "canvas")
|
|
@@ -607,34 +567,25 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
607
567
|
return;
|
|
608
568
|
}
|
|
609
569
|
if (msg.type === "wheel-pan") {
|
|
610
|
-
if (presentation === "canvas" && interactionTool !== "select")
|
|
570
|
+
if (presentation === "canvas" && !sketchActive && interactionTool !== "select")
|
|
611
571
|
panCanvasByWheel(msg.deltaX, msg.deltaY);
|
|
612
572
|
return;
|
|
613
573
|
}
|
|
614
574
|
if (msg.type === "zoom") {
|
|
615
|
-
if (presentation !== "canvas")
|
|
575
|
+
if (presentation !== "canvas" || sketchActive)
|
|
616
576
|
return;
|
|
617
|
-
|
|
618
|
-
const point = iframePointToClientPoint(iframeRect, msg.point, getBoardCamera().zoom);
|
|
619
|
-
zoomAtPointer(point, msg.deltaY);
|
|
577
|
+
zoomAtPointer(toViewportPoint(frame.viewport(), msg.point), msg.deltaY);
|
|
620
578
|
return;
|
|
621
579
|
}
|
|
580
|
+
// The pan moves the frame, so every pan point maps through the viewport captured at pan start.
|
|
622
581
|
const pointInBoard = (point) => {
|
|
623
582
|
const context = framePanContextRef.current;
|
|
624
|
-
|
|
625
|
-
return null;
|
|
626
|
-
return {
|
|
627
|
-
x: context.left + point.x * context.zoom,
|
|
628
|
-
y: context.top + point.y * context.zoom,
|
|
629
|
-
};
|
|
583
|
+
return context ? toViewportPoint(context, point) : null;
|
|
630
584
|
};
|
|
631
585
|
if (msg.type === "pan-start") {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
top: iframeRect.top,
|
|
636
|
-
zoom: getBoardCamera().zoom,
|
|
637
|
-
};
|
|
586
|
+
if (sketchActive)
|
|
587
|
+
return;
|
|
588
|
+
framePanContextRef.current = frame.viewport();
|
|
638
589
|
const point = pointInBoard(msg.point);
|
|
639
590
|
if (point)
|
|
640
591
|
startPanning(point);
|
|
@@ -652,7 +603,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
652
603
|
return;
|
|
653
604
|
}
|
|
654
605
|
if (msg.type === "frame-metadata") {
|
|
655
|
-
const frameDocument = frame.
|
|
606
|
+
const frameDocument = frame.document();
|
|
656
607
|
if (frameDocument)
|
|
657
608
|
disposeInlineTextEdit("route-disposed", frameDocument);
|
|
658
609
|
if (msg.url)
|
|
@@ -683,11 +634,11 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
683
634
|
activateIframeWorkspace(msg.url, { width: boardRef.current?.clientWidth ?? window.innerWidth, height: boardRef.current?.clientHeight ?? window.innerHeight }, { replaceActiveCard: true });
|
|
684
635
|
return;
|
|
685
636
|
}
|
|
686
|
-
const frameDocument = frame.
|
|
637
|
+
const frameDocument = frame.document();
|
|
687
638
|
if (frameDocument)
|
|
688
639
|
disposeInlineTextEdit("route-disposed", frameDocument);
|
|
689
640
|
});
|
|
690
|
-
}, [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]);
|
|
691
642
|
useEffect(() => {
|
|
692
643
|
if (mode === "canvas" && !hasFitAllRan()) {
|
|
693
644
|
if (!fitAllScheduledRef.current) {
|
|
@@ -778,8 +729,13 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
778
729
|
function handleGlobalWheel(e) {
|
|
779
730
|
const zoom = e.ctrlKey || e.metaKey;
|
|
780
731
|
const board = boardRef.current;
|
|
781
|
-
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();
|
|
782
737
|
return;
|
|
738
|
+
}
|
|
783
739
|
const path = e.composedPath();
|
|
784
740
|
// Comment pins overlay the board without being its DOM descendants.
|
|
785
741
|
const overCommentPin = path.some((node) => node instanceof Element && node.matches(".comment-pin"));
|
|
@@ -797,10 +753,11 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
797
753
|
window.addEventListener("wheel", handleGlobalWheel, { capture: true, passive: false });
|
|
798
754
|
return () => window.removeEventListener("wheel", handleGlobalWheel, { capture: true });
|
|
799
755
|
}, [mode, presentation, sketchActive, interactionTool, panCanvasByWheel, zoomAtPointer]);
|
|
800
|
-
|
|
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));
|
|
801
758
|
if (mode !== "canvas")
|
|
802
759
|
return null;
|
|
803
|
-
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: {
|
|
804
761
|
transform: `translate(${camera.x}px, ${camera.y}px) scale(${camera.zoom})`,
|
|
805
762
|
transformOrigin: "0 0",
|
|
806
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 {};
|