nudge-ui 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -46
- package/dist/client.mjs +252 -61
- package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
- package/dist/css/value-semantics/colorSemantics.js +17 -0
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
- package/dist/hosts/astro/clientTransport.js +38 -32
- package/dist/hosts/astro/integration.js +5 -1
- package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
- package/dist/hosts/astro/markdownIdentity.js +81 -0
- package/dist/hosts/next/manifest.js +1 -1
- package/dist/hosts/next/sidecar.js +5 -1
- package/dist/hosts/static/manifest.js +1 -1
- package/dist/hosts/static/server.js +3 -0
- package/dist/hosts/vite/vite.d.ts +3 -0
- package/dist/hosts/vite/vite.js +24 -4
- package/dist/inspector/agent/McpConnectionDialog.js +13 -19
- package/dist/inspector/agent/activity.d.ts +18 -0
- package/dist/inspector/agent/activity.js +70 -0
- package/dist/inspector/agent/client.d.ts +2 -0
- package/dist/inspector/agent/client.js +29 -1
- package/dist/inspector/agent/connectionStatus.js +1 -1
- package/dist/inspector/agent/httpTransport.js +9 -2
- package/dist/inspector/agent/protocol.d.ts +6 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +11 -1
- package/dist/inspector/agent/verification.js +53 -25
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
- package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
- package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
- package/dist/inspector/canvas/CanvasCard.js +133 -38
- package/dist/inspector/canvas/CanvasElementOverlay.js +152 -16
- package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
- package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
- package/dist/inspector/canvas/CanvasToolbar.js +8 -4
- package/dist/inspector/canvas/CanvasWorkspace.js +225 -57
- package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
- package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
- package/dist/inspector/canvas/agentPresentation.js +64 -52
- package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
- package/dist/inspector/canvas/canvasGestures.js +1 -1
- package/dist/inspector/canvas/canvasStore.d.ts +46 -82
- package/dist/inspector/canvas/canvasStore.js +251 -284
- package/dist/inspector/canvas/frameContent.d.ts +21 -0
- package/dist/inspector/canvas/frameContent.js +44 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +21 -4
- package/dist/inspector/canvas/frameProtocol.js +3 -3
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -1
- package/dist/inspector/canvas/keyboardShortcuts.js +4 -4
- package/dist/inspector/canvas/layout.d.ts +52 -0
- package/dist/inspector/canvas/layout.js +106 -0
- package/dist/inspector/canvas/projection.d.ts +15 -5
- package/dist/inspector/canvas/projection.js +69 -13
- package/dist/inspector/canvas/rendererBootstrap.js +32 -4
- package/dist/inspector/canvas/rendererElementSelector.js +47 -6
- package/dist/inspector/canvas/rendererStylesheet.js +2 -2
- package/dist/inspector/canvas/resizeHandleScale.js +1 -1
- package/dist/inspector/canvas/sessionStore.d.ts +11 -26
- package/dist/inspector/canvas/sessionStore.js +43 -183
- package/dist/inspector/canvas/staleChangeDetector.js +9 -3
- package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
- package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
- package/dist/inspector/canvas/viewStore.d.ts +38 -0
- package/dist/inspector/canvas/viewStore.js +104 -0
- package/dist/inspector/canvas/wheelPan.d.ts +8 -0
- package/dist/inspector/canvas/wheelPan.js +8 -0
- package/dist/inspector/changes/changesLog.d.ts +6 -5
- package/dist/inspector/changes/changesLog.js +40 -32
- package/dist/inspector/changes/draftChanges.d.ts +56 -0
- package/dist/inspector/changes/draftChanges.js +300 -0
- package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
- package/dist/inspector/changes/previewDiagnostics.js +11 -25
- package/dist/inspector/clientManifest.d.ts +1 -1
- package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
- package/dist/inspector/comments/CommentsOverlay.js +250 -0
- package/dist/inspector/comments/element.d.ts +6 -0
- package/dist/inspector/comments/element.js +47 -0
- package/dist/inspector/comments/store.d.ts +29 -0
- package/dist/inspector/comments/store.js +121 -0
- package/dist/inspector/comments/verification.d.ts +5 -0
- package/dist/inspector/comments/verification.js +52 -0
- package/dist/inspector/drafts/model.d.ts +35 -0
- package/dist/inspector/drafts/model.js +45 -0
- package/dist/inspector/drafts/store.d.ts +28 -0
- package/dist/inspector/drafts/store.js +231 -0
- package/dist/inspector/index.js +20 -27
- package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
- package/dist/inspector/iterations/capture.d.ts +2 -0
- package/dist/inspector/iterations/capture.js +173 -0
- package/dist/inspector/iterations/client.d.ts +7 -0
- package/dist/inspector/iterations/client.js +52 -0
- package/dist/inspector/iterations/coordinator.d.ts +15 -0
- package/dist/inspector/iterations/coordinator.js +130 -0
- package/dist/inspector/iterations/prompt.d.ts +3 -0
- package/dist/inspector/iterations/prompt.js +26 -0
- package/dist/inspector/overlay/DomNavigator.js +21 -15
- package/dist/inspector/overlay/interactionStyles.js +3 -1
- package/dist/inspector/overlay/spacingGestures.d.ts +13 -1
- package/dist/inspector/overlay/spacingGestures.js +82 -5
- package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
- package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
- package/dist/inspector/projection/renderedInstance.d.ts +2 -1
- package/dist/inspector/projection/renderedInstance.js +9 -5
- package/dist/inspector/projection/structuralProjection.d.ts +2 -1
- package/dist/inspector/projection/structuralProjection.js +19 -15
- package/dist/inspector/projection/textProjection.d.ts +2 -1
- package/dist/inspector/projection/textProjection.js +8 -4
- package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
- package/dist/inspector/prompt/clipboardHandoff.d.ts +10 -18
- package/dist/inspector/prompt/clipboardHandoff.js +92 -143
- package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
- package/dist/inspector/prompt/generatePrompt.js +17 -16
- package/dist/inspector/runtime/runtimeConfig.d.ts +9 -0
- package/dist/inspector/runtime/runtimeConfig.js +24 -0
- package/dist/inspector/settings/SettingsDialog.js +21 -8
- package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
- package/dist/inspector/shell/AgentStatusButton.js +36 -0
- package/dist/inspector/shell/AppShell.js +4 -2
- package/dist/inspector/shell/ChangesLog.js +46 -26
- package/dist/inspector/shell/CopyPromptButton.js +138 -89
- package/dist/inspector/shell/EmptyState.js +9 -8
- package/dist/inspector/shell/InspectorShell.js +13 -21
- package/dist/inspector/shell/PeekOriginalButton.js +24 -23
- package/dist/inspector/shell/originalPreview.js +5 -5
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +11 -0
- package/dist/inspector/sketch/SketchEditor.js +14 -14
- package/dist/inspector/sketch/SketchEntryButton.js +1 -1
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
- package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
- package/dist/inspector/sketch/SketchOverlay.js +7 -7
- package/dist/inspector/sketch/SketchWorkspace.js +18 -12
- package/dist/inspector/sketch/interaction.d.ts +1 -0
- package/dist/inspector/sketch/interaction.js +8 -4
- package/dist/inspector/sketch/model.d.ts +5 -0
- package/dist/inspector/sketch/model.js +6 -2
- package/dist/inspector/sketch/raster.d.ts +1 -2
- package/dist/inspector/sketch/raster.js +68 -4
- package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
- package/dist/inspector/styleEditors/BorderEditor.js +49 -78
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
- package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
- package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
- package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
- package/dist/inspector/styleEditors/ColorPicker.js +10 -4
- package/dist/inspector/styleEditors/GapField.js +3 -5
- package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
- package/dist/inspector/styleEditors/GridChildSection.js +18 -9
- package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
- package/dist/inspector/styleEditors/GridPicker.js +8 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/GridValueField.js +19 -14
- package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
- package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
- package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
- package/dist/inspector/styleEditors/LayoutSection.js +32 -11
- package/dist/inspector/styleEditors/SpacingBox.js +7 -5
- package/dist/inspector/styleEditors/Typography.js +11 -3
- package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
- package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
- package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
- package/dist/inspector/styleEditors/gradientValue.js +114 -0
- package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
- package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
- package/dist/inspector/styleEditors/layoutValue.js +0 -42
- package/dist/inspector/styleEditors/nudgeValue.d.ts +2 -2
- package/dist/inspector/styleEditors/nudgeValue.js +36 -8
- package/dist/inspector/tokens/TokenField.d.ts +7 -0
- package/dist/inspector/tokens/TokenField.js +29 -21
- package/dist/inspector/tokens/TokenLabel.js +5 -14
- package/dist/inspector/tokens/editActions.d.ts +1 -1
- package/dist/inspector/tokens/editActions.js +1 -1
- package/dist/inspector/ui/AtRuleContext.js +4 -6
- package/dist/inspector/ui/Button.d.ts +6 -2
- package/dist/inspector/ui/Button.js +4 -3
- package/dist/inspector/ui/ControlSurface.d.ts +2 -2
- package/dist/inspector/ui/ControlSurface.js +4 -3
- package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
- package/dist/inspector/ui/InlineStyleWarning.js +13 -0
- package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
- package/dist/inspector/ui/InspectorPopover.js +4 -5
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +8 -7
- package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
- package/dist/inspector/ui/SegmentedControl.js +2 -2
- package/dist/inspector/ui/Select.d.ts +6 -1
- package/dist/inspector/ui/Select.js +7 -11
- package/dist/inspector/ui/SideValuesField.js +1 -1
- package/dist/inspector/ui/Tooltip.d.ts +26 -0
- package/dist/inspector/ui/Tooltip.js +32 -0
- package/dist/inspector/ui/classNames.d.ts +2 -0
- package/dist/inspector/ui/classNames.js +4 -0
- package/dist/inspector/ui/customCursors.d.ts +23 -0
- package/dist/inspector/ui/customCursors.js +122 -0
- package/dist/inspector/ui/dragCursor.d.ts +0 -2
- package/dist/inspector/ui/dragCursor.js +2 -10
- package/dist/inspector/ui/portalContainer.d.ts +2 -0
- package/dist/inspector/ui/portalContainer.js +6 -0
- package/dist/inspector/ui/styles.js +29 -21
- package/dist/inspector/workspace/commands.d.ts +9 -0
- package/dist/inspector/workspace/commands.js +82 -0
- package/dist/inspector/workspace/controller.d.ts +4 -0
- package/dist/inspector/workspace/controller.js +66 -0
- package/dist/inspector/workspace/handoff.d.ts +42 -0
- package/dist/inspector/workspace/handoff.js +115 -0
- package/dist/inspector/workspace/timeline.d.ts +30 -0
- package/dist/inspector/workspace/timeline.js +108 -0
- package/dist/internal/agent-protocol.d.ts +19 -2
- package/dist/internal/agent-protocol.js +13 -0
- package/dist/nudge-ui.mjs +558 -311
- package/dist/project/artifacts.d.ts +13 -0
- package/dist/project/artifacts.js +253 -0
- package/dist/transport/artifacts.d.ts +8 -0
- package/dist/transport/artifacts.js +9 -0
- package/dist/transport/editor.js +3 -1
- package/dist/transport/routes.d.ts +2 -0
- package/dist/transport/routes.js +2 -0
- package/package.json +6 -2
- package/dist/inspector/changes/workspaceChanges.d.ts +0 -47
- package/dist/inspector/changes/workspaceChanges.js +0 -265
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { getCanonicalFrameProjection } from "./projection.js";
|
|
3
|
+
import { getDraftForCard } from "../drafts/store.js";
|
|
4
|
+
import { iterationId, contentSourceUrl, getLinkedFrameIds } from "./frameContent.js";
|
|
2
5
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
6
|
import { IconCornerLeftUp } from "@tabler/icons-react";
|
|
4
|
-
import { useCanvasCards, activateIframeWorkspace,
|
|
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
|
+
import { setBoardCamera, getBoardCamera, setCanvasPresentation, hasFitAllRan, useBoardCamera, useCanvasPresentation, useCanvasPresentationTransitioning, useCanvasLayoutTransitioning, useTemporaryAppInteraction, setTemporaryAppInteraction, } from "./viewStore.js";
|
|
5
9
|
import { CanvasCard } from "./CanvasCard.js";
|
|
10
|
+
import { useCanvasLayoutAnimation } from "./useCanvasLayoutAnimation.js";
|
|
6
11
|
import { Button } from "../ui/Button.js";
|
|
7
|
-
import { useCanvasMode } from "./
|
|
12
|
+
import { useCanvasMode } from "./viewStore.js";
|
|
8
13
|
import { subscribeChanges } from "../changes/changesLog.js";
|
|
9
14
|
import { subscribeOriginalPreview, isOriginalPreviewActive } from "../shell/originalPreview.js";
|
|
10
15
|
import { recordCanvasStructuralProjectionReports } from "../projection/structuralProjection.js";
|
|
@@ -12,31 +17,37 @@ import { recordCanvasRenderedInstanceProjectionReports } from "../projection/ren
|
|
|
12
17
|
import { recordCanvasTextProjectionReports } from "../projection/textProjection.js";
|
|
13
18
|
import { getChangesList, isPreviewableChange } from "../changes/changesLog.js";
|
|
14
19
|
import { changeKey } from "../changes/model.js";
|
|
15
|
-
import {
|
|
20
|
+
import { getActiveDraftChanges } from "../changes/draftChanges.js";
|
|
16
21
|
import { beginPreviewAttempt, publishPreviewDiagnostic } from "../changes/previewDiagnostics.js";
|
|
17
22
|
import { verifyManagedStyleProjection } from "../changes/managedStyleProjection.js";
|
|
18
23
|
import { getCanvasPreviewDocument, getRegisteredFrames, isCanvasCanonicalProjectionRevisionCurrent, PROJECT_ID, projectToAllReadyCards, recordCanvasProjectionApplied, WORKSPACE_ID, } from "./projection.js";
|
|
19
|
-
import { normalizeUrl } from "./normalizeUrl.js";
|
|
24
|
+
import { normalizeUrl, normalizedUrlKey } from "./normalizeUrl.js";
|
|
20
25
|
import { createNudgeUiEditorUrl } from "../../transport/editor.js";
|
|
21
26
|
import { disposeInlineTextEdit } from "../inline-text/inlineTextEditor.js";
|
|
22
27
|
import { PROTOCOL_VERSION, isProjectionAppliedMessage, isKeyboardShortcutMessage, isRenderedInstanceProjectionReportMessage, isTextProjectionReportMessage, isStructuralProjectionReportMessage, } from "./frameProtocol.js";
|
|
28
|
+
import { wheelPanDelta } from "./wheelPan.js";
|
|
23
29
|
import { iframePointToClientPoint, zoomCameraAtPointer } from "./canvasGestures.js";
|
|
24
|
-
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.is-presentation-transitioning .canvas-workspace__board-content {\n transition: transform 200ms ease;\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.is-presentation-card {\n transition: left 200ms ease, top 200ms ease;\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@media (prefers-reduced-motion: reduce) {\n .canvas-workspace.is-presentation-transitioning .canvas-workspace__board-content,\n .canvas-workspace.is-presentation-transitioning .canvas-card.is-presentation-card {\n transition: none;\n }\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";
|
|
25
|
-
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-
|
|
26
|
-
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\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";
|
|
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.is-presentation-transitioning .canvas-workspace__board-content {\n transition: transform 200ms ease;\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.is-presentation-card {\n transition: left 200ms ease, top 200ms ease;\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@media (prefers-reduced-motion: reduce) {\n .canvas-workspace.is-presentation-transitioning .canvas-workspace__board-content,\n .canvas-workspace.is-presentation-transitioning .canvas-card.is-presentation-card {\n transition: none;\n }\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";
|
|
31
|
+
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
|
+
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";
|
|
27
33
|
import { useInspectorOpen } from "../shell/openStore.js";
|
|
28
34
|
import { isEditableEvent } from "../shell/shortcuts.js";
|
|
29
35
|
import { acknowledgeAgentRendererReady } from "./agentPresentation.js";
|
|
30
36
|
import { useInspectorSession } from "../session/sessionContext.js";
|
|
37
|
+
import { isNudgeUiFeatureEnabled } from "../runtime/runtimeConfig.js";
|
|
31
38
|
import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
|
|
32
39
|
import { subscribeCanvasRendererMessages } from "./rendererMessageRouter.js";
|
|
33
40
|
import { adjacentCanvasZoomLevel, CanvasToolbar, } from "./CanvasToolbar.js";
|
|
34
|
-
import { canvasToolForEvent, canvasToolForCode } from "./keyboardShortcuts.js";
|
|
35
|
-
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:
|
|
41
|
+
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";
|
|
36
43
|
import { getActiveCanvasFrame } from "./activeCanvasDocument.js";
|
|
37
44
|
import { startSketchCapture } from "../sketch/SketchWorkspace.js";
|
|
38
45
|
import { cancelSketchInteraction, useSketchInteractionActive } from "../sketch/interaction.js";
|
|
39
46
|
import { isNudgeUiDev } from "../runtime/devFlag.js";
|
|
47
|
+
import { createCommentCursor, DESIGN_SELECT_CURSOR, PAN_CURSOR } from "../ui/customCursors.js";
|
|
48
|
+
import { addLinkedFrame, createIteration as createWorkspaceIteration, removeFrame } from "../workspace/commands.js";
|
|
49
|
+
import { getSelectedElements, setSelectedElement } from "../selection/selectionStore.js";
|
|
50
|
+
import { setCommentToolActive } from "../comments/store.js";
|
|
40
51
|
const WORKSPACE_STYLES = [foundationStyles, canvasWorkspaceStyles, canvasCardStyles, canvasToolbarStyles].join("\n");
|
|
41
52
|
const PRESENTATION_TRANSITION_MS = 200;
|
|
42
53
|
function snapshotRect(rect) {
|
|
@@ -60,8 +71,10 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
60
71
|
const focusedCardId = useFocusedCardId();
|
|
61
72
|
const sketchActive = useSketchInteractionActive();
|
|
62
73
|
const sketchEnabled = isNudgeUiDev() && runtimeConfig.demo !== true;
|
|
74
|
+
const canvasIterationsEnabled = isNudgeUiFeatureEnabled(runtimeConfig.features, "canvasIterations");
|
|
63
75
|
const sketchAvailable = sketchEnabled && getActiveCanvasFrame() !== null;
|
|
64
76
|
const boardRef = useRef(null);
|
|
77
|
+
useCanvasLayoutAnimation(boardRef, cards, camera, presentation);
|
|
65
78
|
const panningRef = useRef(false);
|
|
66
79
|
const panOriginRef = useRef({ x: 0, y: 0 });
|
|
67
80
|
const cameraAtPanStartRef = useRef({ x: 0, y: 0 });
|
|
@@ -74,10 +87,24 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
74
87
|
const focusCardRectRef = useRef(null);
|
|
75
88
|
const canvasEntryAnimationRef = useRef(null);
|
|
76
89
|
const [boardCursorClass, setBoardCursorClass] = useState("");
|
|
77
|
-
const [
|
|
78
|
-
const
|
|
90
|
+
const [selectedTool, setInteractionTool] = useState("design");
|
|
91
|
+
const temporaryAppInteraction = useTemporaryAppInteraction();
|
|
92
|
+
const interactionTool = temporaryAppInteraction ? "select" : selectedTool;
|
|
93
|
+
const layoutTransitioning = useCanvasLayoutTransitioning();
|
|
94
|
+
const rendererInteractionsEnabled = interactionTool !== "select" && !layoutTransitioning;
|
|
95
|
+
const rendererCursor = interactionTool === "comment" ? "comment" : "design";
|
|
96
|
+
const boardCursor = boardCursorClass === "is-grabbing"
|
|
97
|
+
? "grabbing"
|
|
98
|
+
: boardCursorClass !== ""
|
|
99
|
+
? PAN_CURSOR
|
|
100
|
+
: interactionTool === "comment"
|
|
101
|
+
? createCommentCursor(boardRef.current)
|
|
102
|
+
: interactionTool === "design"
|
|
103
|
+
? DESIGN_SELECT_CURSOR
|
|
104
|
+
: undefined;
|
|
79
105
|
const linksOpenInCards = interactionTool === "select" && presentation === "canvas";
|
|
80
106
|
const presentationCardId = selectedCardId ?? focusedCardId ?? cards[0]?.id ?? null;
|
|
107
|
+
const linkedFrameIds = getLinkedFrameIds(cards, selectedCardId);
|
|
81
108
|
useLayoutEffect(() => {
|
|
82
109
|
const board = boardRef.current;
|
|
83
110
|
const cardElement = presentationCardId
|
|
@@ -157,7 +184,16 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
157
184
|
width: board.clientWidth,
|
|
158
185
|
height: board.clientHeight,
|
|
159
186
|
};
|
|
160
|
-
const primaryCard = activateIframeWorkspace(primaryUrl, viewport
|
|
187
|
+
const primaryCard = activateIframeWorkspace(primaryUrl, viewport, {
|
|
188
|
+
replaceActiveCard: presentation === "focus" && runtimeConfig.demo !== true,
|
|
189
|
+
preserveIterationFocus: true,
|
|
190
|
+
});
|
|
191
|
+
if (primaryCard && cards.length === 0 && runtimeConfig.demo !== true) {
|
|
192
|
+
setBoardCamera({
|
|
193
|
+
x: 40, y: 120,
|
|
194
|
+
zoom: Math.max(0.25, Math.min(0.9, (viewport.width - 80) / primaryCard.width, (viewport.height - 160) / primaryCard.height)),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
161
197
|
if (runtimeConfig.demo === true && primaryCard && !demoSeededRef.current) {
|
|
162
198
|
demoSeededRef.current = true;
|
|
163
199
|
const demoPages = runtimeConfig.demoPages ?? [];
|
|
@@ -201,7 +237,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
201
237
|
}
|
|
202
238
|
focusCard(primaryCard.id);
|
|
203
239
|
}
|
|
204
|
-
}, [primaryUrl, cards.length, runtimeConfig.demo, runtimeConfig.demoPages, runtimeConfig.demoCardLabels]);
|
|
240
|
+
}, [primaryUrl, cards.length, presentation, runtimeConfig.demo, runtimeConfig.demoPages, runtimeConfig.demoCardLabels]);
|
|
205
241
|
useEffect(() => {
|
|
206
242
|
if (primaryUrl && activatedTargetRef.current !== primaryUrl)
|
|
207
243
|
return;
|
|
@@ -209,7 +245,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
209
245
|
const activeCard = cards.find((card) => card.id === activeCardId);
|
|
210
246
|
if (!activeCard)
|
|
211
247
|
return;
|
|
212
|
-
const editorUrl = createNudgeUiEditorUrl(activeCard.
|
|
248
|
+
const editorUrl = createNudgeUiEditorUrl(contentSourceUrl(activeCard.content));
|
|
213
249
|
if (window.location.href !== editorUrl) {
|
|
214
250
|
window.history.replaceState(window.history.state, "", editorUrl);
|
|
215
251
|
}
|
|
@@ -222,9 +258,10 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
222
258
|
workspaceId: WORKSPACE_ID,
|
|
223
259
|
cardId,
|
|
224
260
|
spaceHeld,
|
|
261
|
+
cursor: spaceHeld ? "drag" : rendererCursor,
|
|
225
262
|
}, window.location.origin);
|
|
226
|
-
}, []);
|
|
227
|
-
const sendInspectorInteractionState = useCallback((iframe, cardId, open, interactionsEnabled) => {
|
|
263
|
+
}, [rendererCursor]);
|
|
264
|
+
const sendInspectorInteractionState = useCallback((iframe, cardId, open, interactionsEnabled, cursor) => {
|
|
228
265
|
iframe.contentWindow?.postMessage({
|
|
229
266
|
type: "inspector-interaction-state",
|
|
230
267
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -233,6 +270,8 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
233
270
|
cardId,
|
|
234
271
|
open,
|
|
235
272
|
interactionsEnabled,
|
|
273
|
+
cursor,
|
|
274
|
+
commentCursor: cursor === "comment" ? createCommentCursor(boardRef.current) : undefined,
|
|
236
275
|
}, window.location.origin);
|
|
237
276
|
}, []);
|
|
238
277
|
const sendLinkTargetState = useCallback((iframe, cardId, openInCard) => {
|
|
@@ -253,15 +292,16 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
253
292
|
workspaceId: WORKSPACE_ID,
|
|
254
293
|
cardId,
|
|
255
294
|
enabled,
|
|
295
|
+
panScroll: enabled && interactionTool !== "select",
|
|
256
296
|
}, window.location.origin);
|
|
257
|
-
}, []);
|
|
297
|
+
}, [interactionTool]);
|
|
258
298
|
useEffect(() => {
|
|
259
299
|
for (const [cardId, iframe] of getRegisteredFrames()) {
|
|
260
|
-
sendInspectorInteractionState(iframe, cardId, inspectorOpen, rendererInteractionsEnabled);
|
|
300
|
+
sendInspectorInteractionState(iframe, cardId, inspectorOpen, rendererInteractionsEnabled, rendererCursor);
|
|
261
301
|
sendBoardGestureState(iframe, cardId, presentation === "canvas");
|
|
262
302
|
sendLinkTargetState(iframe, cardId, linksOpenInCards);
|
|
263
303
|
}
|
|
264
|
-
}, [inspectorOpen, linksOpenInCards, presentation, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState, sendLinkTargetState]);
|
|
304
|
+
}, [inspectorOpen, linksOpenInCards, presentation, rendererCursor, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState, sendLinkTargetState]);
|
|
265
305
|
const broadcastPanModifier = useCallback((spaceHeld) => {
|
|
266
306
|
for (const [cardId, iframe] of getRegisteredFrames()) {
|
|
267
307
|
sendPanModifier(iframe, cardId, spaceHeld);
|
|
@@ -273,6 +313,12 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
273
313
|
return;
|
|
274
314
|
setBoardCamera(zoomCameraAtPointer(getBoardCamera(), point, board.getBoundingClientRect(), deltaY));
|
|
275
315
|
}, []);
|
|
316
|
+
const panCanvasByWheel = useCallback((deltaX, deltaY) => {
|
|
317
|
+
if (!Number.isFinite(deltaX) || !Number.isFinite(deltaY))
|
|
318
|
+
return;
|
|
319
|
+
const camera = getBoardCamera();
|
|
320
|
+
setBoardCamera({ ...camera, x: camera.x - deltaX, y: camera.y - deltaY });
|
|
321
|
+
}, []);
|
|
276
322
|
const fitCanvasToBoard = useCallback(() => {
|
|
277
323
|
const board = boardRef.current;
|
|
278
324
|
fitAllCards(board ? { width: board.clientWidth, height: board.clientHeight } : undefined);
|
|
@@ -310,6 +356,14 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
310
356
|
setBoardCursorClass("");
|
|
311
357
|
}, [broadcastPanModifier, endPanning]);
|
|
312
358
|
const handleToolChange = useCallback((nextTool) => {
|
|
359
|
+
if (nextTool === "select" && selectedTool === "select") {
|
|
360
|
+
setInteractionTool("design");
|
|
361
|
+
stopPanMode();
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (nextTool === "select") {
|
|
365
|
+
setSelectedElement(null);
|
|
366
|
+
}
|
|
313
367
|
if (nextTool === "sketch") {
|
|
314
368
|
if (!sketchEnabled)
|
|
315
369
|
return;
|
|
@@ -317,27 +371,52 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
317
371
|
if (!hostElement)
|
|
318
372
|
return;
|
|
319
373
|
stopPanMode();
|
|
374
|
+
setCommentToolActive(false);
|
|
320
375
|
setInteractionTool(nextTool);
|
|
321
376
|
startSketchCapture(hostElement);
|
|
322
377
|
return;
|
|
323
378
|
}
|
|
324
379
|
if (sketchActive)
|
|
325
380
|
cancelSketchInteraction();
|
|
326
|
-
|
|
327
|
-
if (presentation === "focus") {
|
|
328
|
-
const board = boardRef.current;
|
|
329
|
-
setCanvasPresentation("canvas", board ? { width: board.clientWidth, height: board.clientHeight } : undefined);
|
|
330
|
-
}
|
|
331
|
-
setInteractionTool(nextTool);
|
|
332
|
-
spaceHeldRef.current = true;
|
|
333
|
-
broadcastPanModifier(true);
|
|
334
|
-
if (!panningRef.current)
|
|
335
|
-
setBoardCursorClass("is-grabbable");
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
381
|
+
setCommentToolActive(nextTool === "comment");
|
|
338
382
|
setInteractionTool(nextTool);
|
|
339
383
|
stopPanMode();
|
|
340
|
-
}, [
|
|
384
|
+
}, [selectedTool, sketchActive, sketchEnabled, stopPanMode]);
|
|
385
|
+
useEffect(() => () => setCommentToolActive(false), []);
|
|
386
|
+
useEffect(() => {
|
|
387
|
+
setCommentToolActive(interactionTool === "comment");
|
|
388
|
+
if (temporaryAppInteraction)
|
|
389
|
+
stopPanMode();
|
|
390
|
+
}, [interactionTool, temporaryAppInteraction, stopPanMode]);
|
|
391
|
+
useEffect(() => {
|
|
392
|
+
const onKeyDown = (event) => {
|
|
393
|
+
if (mode === "canvas" && event.code === APP_INTERACTION_KEY && !event.repeat && !isEditableEvent(event))
|
|
394
|
+
setTemporaryAppInteraction(true);
|
|
395
|
+
};
|
|
396
|
+
const onKeyUp = (event) => {
|
|
397
|
+
if (event.code === APP_INTERACTION_KEY)
|
|
398
|
+
setTemporaryAppInteraction(false);
|
|
399
|
+
};
|
|
400
|
+
const onBlur = () => {
|
|
401
|
+
if (!document.hasFocus())
|
|
402
|
+
setTemporaryAppInteraction(false);
|
|
403
|
+
};
|
|
404
|
+
const onVisibility = () => {
|
|
405
|
+
if (document.visibilityState === "hidden")
|
|
406
|
+
setTemporaryAppInteraction(false);
|
|
407
|
+
};
|
|
408
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
409
|
+
window.addEventListener("keyup", onKeyUp, true);
|
|
410
|
+
window.addEventListener("blur", onBlur);
|
|
411
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
412
|
+
return () => {
|
|
413
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
414
|
+
window.removeEventListener("keyup", onKeyUp, true);
|
|
415
|
+
window.removeEventListener("blur", onBlur);
|
|
416
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
417
|
+
setTemporaryAppInteraction(false);
|
|
418
|
+
};
|
|
419
|
+
}, [mode]);
|
|
341
420
|
useEffect(() => {
|
|
342
421
|
if (mode !== "canvas")
|
|
343
422
|
return;
|
|
@@ -356,8 +435,21 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
356
435
|
const handleZoomStep = useCallback((direction) => {
|
|
357
436
|
if (presentation === "focus") {
|
|
358
437
|
if (direction < 0) {
|
|
359
|
-
|
|
360
|
-
|
|
438
|
+
batchCanvasChanges(() => {
|
|
439
|
+
const board = boardRef.current;
|
|
440
|
+
const cards = getCanvasCards();
|
|
441
|
+
const activeId = getSelectedCardId() ?? getFocusedCardId();
|
|
442
|
+
const card = cards.find((candidate) => candidate.id === activeId) ?? cards[0];
|
|
443
|
+
if (board && card) {
|
|
444
|
+
const zoom = 0.9;
|
|
445
|
+
setBoardCamera({
|
|
446
|
+
x: board.clientWidth / 2 - (card.x + card.width / 2) * zoom,
|
|
447
|
+
y: board.clientHeight / 2 - (card.y + card.height / 2) * zoom,
|
|
448
|
+
zoom,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
setCanvasPresentation("canvas");
|
|
452
|
+
});
|
|
361
453
|
}
|
|
362
454
|
return;
|
|
363
455
|
}
|
|
@@ -373,6 +465,23 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
373
465
|
focusCard(cardId);
|
|
374
466
|
setCanvasPresentation("focus");
|
|
375
467
|
}, []);
|
|
468
|
+
const duplicateDesign = useCallback((cardId) => {
|
|
469
|
+
const duplicate = addLinkedFrame(cardId);
|
|
470
|
+
if (!duplicate)
|
|
471
|
+
return;
|
|
472
|
+
setSelectedElement(null);
|
|
473
|
+
setCanvasPresentation("canvas");
|
|
474
|
+
const board = boardRef.current;
|
|
475
|
+
focusCanvasCards([duplicate.id], board ? { width: board.clientWidth, height: board.clientHeight } : undefined);
|
|
476
|
+
}, []);
|
|
477
|
+
const createIteration = useCallback(async (cardId, position) => {
|
|
478
|
+
const iteration = await createWorkspaceIteration(cardId, position);
|
|
479
|
+
setSelectedElement(null);
|
|
480
|
+
const board = boardRef.current;
|
|
481
|
+
if (board && !position)
|
|
482
|
+
focusCanvasCards([iteration.id], { width: board.clientWidth, height: board.clientHeight });
|
|
483
|
+
return iteration.id;
|
|
484
|
+
}, []);
|
|
376
485
|
useEffect(() => {
|
|
377
486
|
if (!sketchActive && interactionTool === "sketch") {
|
|
378
487
|
setInteractionTool("design");
|
|
@@ -402,6 +511,11 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
402
511
|
return subscribeCanvasRendererMessages(({ cardId, iframe, identity, message: msg }) => {
|
|
403
512
|
if (msg.type === "renderer-hello")
|
|
404
513
|
return;
|
|
514
|
+
if (msg.type === "app-interaction-modifier") {
|
|
515
|
+
if (mode === "canvas")
|
|
516
|
+
setTemporaryAppInteraction(msg.held === true);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
405
519
|
if (isKeyboardShortcutMessage(msg, identity)) {
|
|
406
520
|
if (msg.phase === "keydown") {
|
|
407
521
|
const nextTool = canvasToolForCode(msg.code);
|
|
@@ -412,11 +526,14 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
412
526
|
}
|
|
413
527
|
const frame = { cardId, iframe };
|
|
414
528
|
const syncActiveFrameUrl = (url) => {
|
|
415
|
-
|
|
416
|
-
const activeCardId = getSelectedCardId() ?? getFocusedCardId() ?? getCanvasCards()[0]?.id;
|
|
417
|
-
if (activeCardId !== frame.cardId && getCanvasCards().length !== 1)
|
|
529
|
+
if (getCanvasCards().some((card) => card.id === frame.cardId && iterationId(card.content)))
|
|
418
530
|
return;
|
|
419
|
-
|
|
531
|
+
const activeCardId = getSelectedCardId() ?? getFocusedCardId() ?? getCanvasCards()[0]?.id;
|
|
532
|
+
if (activeCardId === frame.cardId || getCanvasCards().length === 1) {
|
|
533
|
+
activatedTargetRef.current = url;
|
|
534
|
+
window.history.replaceState(window.history.state, "", createNudgeUiEditorUrl(url));
|
|
535
|
+
}
|
|
536
|
+
updateCardUrl(frame.cardId, url);
|
|
420
537
|
};
|
|
421
538
|
if (isProjectionAppliedMessage(msg, identity)) {
|
|
422
539
|
recordCanvasProjectionApplied(frame.cardId, msg.revision);
|
|
@@ -425,10 +542,12 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
425
542
|
if (isOriginalPreviewActive())
|
|
426
543
|
return;
|
|
427
544
|
const frameDocument = frame.iframe.contentDocument;
|
|
428
|
-
|
|
429
|
-
|
|
545
|
+
const plan = getCanonicalFrameProjection(frame.cardId, msg.revision);
|
|
546
|
+
const draft = getDraftForCard(frame.cardId);
|
|
547
|
+
if (frameDocument && plan && draft && isCanvasCanonicalProjectionRevisionCurrent(frameDocument, msg.revision)) {
|
|
548
|
+
const attempt = beginPreviewAttempt(getCanvasPreviewDocument(frame.cardId), plan.draftRevision);
|
|
430
549
|
if (attempt) {
|
|
431
|
-
for (const change of
|
|
550
|
+
for (const change of draft.contents.changes) {
|
|
432
551
|
if (!isPreviewableChange(change))
|
|
433
552
|
continue;
|
|
434
553
|
const result = verifyManagedStyleProjection(change, null, frameDocument);
|
|
@@ -450,15 +569,21 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
450
569
|
return;
|
|
451
570
|
}
|
|
452
571
|
if (isStructuralProjectionReportMessage(msg, identity)) {
|
|
453
|
-
|
|
572
|
+
const plan = getCanonicalFrameProjection(frame.cardId, msg.revision);
|
|
573
|
+
if (plan)
|
|
574
|
+
recordCanvasStructuralProjectionReports(frame.cardId, msg.revision, msg.reports, plan);
|
|
454
575
|
return;
|
|
455
576
|
}
|
|
456
577
|
if (isRenderedInstanceProjectionReportMessage(msg, identity)) {
|
|
457
|
-
|
|
578
|
+
const plan = getCanonicalFrameProjection(frame.cardId, msg.revision);
|
|
579
|
+
if (plan)
|
|
580
|
+
recordCanvasRenderedInstanceProjectionReports(frame.cardId, msg.revision, msg.reports, plan);
|
|
458
581
|
return;
|
|
459
582
|
}
|
|
460
583
|
if (isTextProjectionReportMessage(msg, identity)) {
|
|
461
|
-
|
|
584
|
+
const plan = getCanonicalFrameProjection(frame.cardId, msg.revision);
|
|
585
|
+
if (plan)
|
|
586
|
+
recordCanvasTextProjectionReports(frame.cardId, msg.revision, msg.reports, plan);
|
|
462
587
|
return;
|
|
463
588
|
}
|
|
464
589
|
if (msg.type === "frame-ready") {
|
|
@@ -467,7 +592,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
467
592
|
// A card can finish loading after Space was pressed on the controller.
|
|
468
593
|
// Seed it with the current modifier state before its first pointer event.
|
|
469
594
|
sendPanModifier(frame.iframe, frame.cardId, spaceHeldRef.current);
|
|
470
|
-
sendInspectorInteractionState(frame.iframe, frame.cardId, inspectorOpen, rendererInteractionsEnabled);
|
|
595
|
+
sendInspectorInteractionState(frame.iframe, frame.cardId, inspectorOpen, rendererInteractionsEnabled, spaceHeldRef.current ? "drag" : rendererCursor);
|
|
471
596
|
sendBoardGestureState(frame.iframe, frame.cardId, presentation === "canvas");
|
|
472
597
|
sendLinkTargetState(frame.iframe, frame.cardId, linksOpenInCards);
|
|
473
598
|
}
|
|
@@ -481,6 +606,11 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
481
606
|
broadcastPanModifier(msg.spaceHeld);
|
|
482
607
|
return;
|
|
483
608
|
}
|
|
609
|
+
if (msg.type === "wheel-pan") {
|
|
610
|
+
if (presentation === "canvas" && interactionTool !== "select")
|
|
611
|
+
panCanvasByWheel(msg.deltaX, msg.deltaY);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
484
614
|
if (msg.type === "zoom") {
|
|
485
615
|
if (presentation !== "canvas")
|
|
486
616
|
return;
|
|
@@ -531,19 +661,33 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
531
661
|
}
|
|
532
662
|
if (msg.type !== "navigation-intent")
|
|
533
663
|
return;
|
|
664
|
+
if (getCanvasCards().some((card) => card.id === frame.cardId && iterationId(card.content)))
|
|
665
|
+
return;
|
|
534
666
|
const normalized = normalizeUrl(msg.url);
|
|
535
667
|
if (!normalized || normalized.origin !== window.location.origin)
|
|
536
668
|
return;
|
|
537
|
-
if (msg.openInCard === true) {
|
|
538
|
-
const card =
|
|
669
|
+
if (msg.openInCard === true && presentation === "canvas") {
|
|
670
|
+
const card = getCanvasCards().find((candidate) => {
|
|
671
|
+
const route = !iterationId(candidate.content) && normalizeUrl(contentSourceUrl(candidate.content));
|
|
672
|
+
return route && normalizedUrlKey(route) === normalizedUrlKey(normalized);
|
|
673
|
+
}) ?? addCanvasCard(msg.url);
|
|
539
674
|
selectCard(card.id);
|
|
675
|
+
const board = boardRef.current;
|
|
676
|
+
if (board)
|
|
677
|
+
focusCanvasCards([card.id], { width: board.clientWidth, height: board.clientHeight });
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
if (msg.openInCard === true) {
|
|
681
|
+
// A delayed renderer message must not create a new frame after entering focus mode.
|
|
682
|
+
selectCard(frame.cardId);
|
|
683
|
+
activateIframeWorkspace(msg.url, { width: boardRef.current?.clientWidth ?? window.innerWidth, height: boardRef.current?.clientHeight ?? window.innerHeight }, { replaceActiveCard: true });
|
|
540
684
|
return;
|
|
541
685
|
}
|
|
542
686
|
const frameDocument = frame.iframe.contentDocument;
|
|
543
687
|
if (frameDocument)
|
|
544
688
|
disposeInlineTextEdit("route-disposed", frameDocument);
|
|
545
689
|
});
|
|
546
|
-
}, [broadcastPanModifier, endPanning, handleToolChange, inspectorOpen, linksOpenInCards, movePanning, presentation, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState, sendLinkTargetState, sendPanModifier, sketchAvailable, startPanning, zoomAtPointer]);
|
|
690
|
+
}, [broadcastPanModifier, endPanning, handleToolChange, interactionTool, mode, panCanvasByWheel, inspectorOpen, linksOpenInCards, movePanning, presentation, rendererCursor, rendererInteractionsEnabled, sendBoardGestureState, sendInspectorInteractionState, sendLinkTargetState, sendPanModifier, sketchAvailable, startPanning, zoomAtPointer]);
|
|
547
691
|
useEffect(() => {
|
|
548
692
|
if (mode === "canvas" && !hasFitAllRan()) {
|
|
549
693
|
if (!fitAllScheduledRef.current) {
|
|
@@ -557,14 +701,16 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
557
701
|
}, [mode, cards.length, inspectorOpen, fitCanvasToBoard]);
|
|
558
702
|
useEffect(() => {
|
|
559
703
|
function onKeyDown(e) {
|
|
560
|
-
if (mode !== "canvas" || presentation !== "canvas" || sketchActive)
|
|
704
|
+
if (mode !== "canvas" || presentation !== "canvas" || sketchActive || interactionTool === "select")
|
|
561
705
|
return;
|
|
562
706
|
if ((e.key === "Delete" || e.key === "Backspace") && !isEditableEvent(e)) {
|
|
707
|
+
if (e.defaultPrevented || getSelectedElements().length > 0 || getCanvasCards().length < 2)
|
|
708
|
+
return;
|
|
563
709
|
const selectedCardId = getSelectedCardId();
|
|
564
710
|
if (!selectedCardId)
|
|
565
711
|
return;
|
|
566
712
|
e.preventDefault();
|
|
567
|
-
|
|
713
|
+
removeFrame(selectedCardId);
|
|
568
714
|
return;
|
|
569
715
|
}
|
|
570
716
|
if (e.code === "Space" && !e.repeat) {
|
|
@@ -580,7 +726,7 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
580
726
|
}
|
|
581
727
|
function onKeyUp(e) {
|
|
582
728
|
if (e.code === "Space") {
|
|
583
|
-
spaceHeldRef.current =
|
|
729
|
+
spaceHeldRef.current = false;
|
|
584
730
|
broadcastPanModifier(spaceHeldRef.current);
|
|
585
731
|
if (!panningRef.current) {
|
|
586
732
|
setBoardCursorClass(spaceHeldRef.current ? "is-grabbable" : "");
|
|
@@ -594,6 +740,15 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
594
740
|
window.removeEventListener("keyup", onKeyUp, true);
|
|
595
741
|
};
|
|
596
742
|
}, [broadcastPanModifier, interactionTool, mode, presentation, sketchActive]);
|
|
743
|
+
const handleBoardClick = useCallback((event) => {
|
|
744
|
+
if (presentation !== "canvas" || sketchActive || spaceHeldRef.current || panningRef.current)
|
|
745
|
+
return;
|
|
746
|
+
if (event.target === event.currentTarget
|
|
747
|
+
|| (event.target instanceof HTMLElement && event.target.dataset.test === "canvas-board-content")) {
|
|
748
|
+
setSelectedElement(null);
|
|
749
|
+
deselectCard();
|
|
750
|
+
}
|
|
751
|
+
}, [presentation, sketchActive]);
|
|
597
752
|
const handleBoardPointerDown = useCallback((e) => {
|
|
598
753
|
if (presentation !== "canvas" || sketchActive)
|
|
599
754
|
return;
|
|
@@ -618,22 +773,35 @@ export function CanvasWorkspace({ primaryUrl }) {
|
|
|
618
773
|
window.addEventListener("pointerup", onUp);
|
|
619
774
|
}, [endPanning, movePanning, presentation, sketchActive, startPanning]);
|
|
620
775
|
useEffect(() => {
|
|
621
|
-
if (mode !== "canvas" || presentation !== "canvas"
|
|
776
|
+
if (mode !== "canvas" || presentation !== "canvas")
|
|
622
777
|
return;
|
|
623
778
|
function handleGlobalWheel(e) {
|
|
624
|
-
|
|
779
|
+
const zoom = e.ctrlKey || e.metaKey;
|
|
780
|
+
const board = boardRef.current;
|
|
781
|
+
if (zoom && sketchActive)
|
|
782
|
+
return;
|
|
783
|
+
const path = e.composedPath();
|
|
784
|
+
// Comment pins overlay the board without being its DOM descendants.
|
|
785
|
+
const overCommentPin = path.some((node) => node instanceof Element && node.matches(".comment-pin"));
|
|
786
|
+
if (!zoom && (interactionTool === "select" || !board || (!path.includes(board) && !overCommentPin)))
|
|
625
787
|
return;
|
|
626
788
|
e.preventDefault();
|
|
627
789
|
e.stopPropagation();
|
|
628
|
-
|
|
790
|
+
if (zoom)
|
|
791
|
+
zoomAtPointer({ x: e.clientX, y: e.clientY }, e.deltaY);
|
|
792
|
+
else if (board) {
|
|
793
|
+
const delta = wheelPanDelta(e, { width: board.clientWidth, height: board.clientHeight });
|
|
794
|
+
panCanvasByWheel(delta.x, delta.y);
|
|
795
|
+
}
|
|
629
796
|
}
|
|
630
797
|
window.addEventListener("wheel", handleGlobalWheel, { capture: true, passive: false });
|
|
631
798
|
return () => window.removeEventListener("wheel", handleGlobalWheel, { capture: true });
|
|
632
|
-
}, [mode, presentation, sketchActive, zoomAtPointer]);
|
|
799
|
+
}, [mode, presentation, sketchActive, interactionTool, panCanvasByWheel, zoomAtPointer]);
|
|
800
|
+
const renderCard = (card) => (_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));
|
|
633
801
|
if (mode !== "canvas")
|
|
634
802
|
return null;
|
|
635
|
-
return (_jsxs(_Fragment, { children: [_jsx("style", { "data-test": "canvas-styles", children: WORKSPACE_STYLES }), _jsx("div", { className: `canvas-workspace${presentationTransitioning ? " is-presentation-transitioning" : ""}`, "data-test": "canvas-workspace", "data-presentation": presentation, style: { right: inspectorOpen ? "min(320px, 100vw)" : 0 }, children: _jsxs("div", { className: `canvas-workspace__board ${boardCursorClass}`.trim(), "data-test": "canvas-board", ref: boardRef, onPointerDown: handleBoardPointerDown, 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" ? (_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: {
|
|
803
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { "data-test": "canvas-styles", children: WORKSPACE_STYLES }), _jsx("div", { className: `canvas-workspace${presentationTransitioning ? " is-presentation-transitioning" : ""}`, "data-test": "canvas-workspace", "data-presentation": presentation, 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" ? (_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: {
|
|
636
804
|
transform: `translate(${camera.x}px, ${camera.y}px) scale(${camera.zoom})`,
|
|
637
805
|
transformOrigin: "0 0",
|
|
638
|
-
}, "data-test": "canvas-board-content", children: cards.map(
|
|
806
|
+
}, "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] }));
|
|
639
807
|
}
|
|
@@ -3,8 +3,6 @@ import { useSyncExternalStore } from "react";
|
|
|
3
3
|
import { isPreviewableChange, isTokenChange } from "../changes/changesLog.js";
|
|
4
4
|
import { changeKey } from "../changes/model.js";
|
|
5
5
|
import { getAnyPreviewDiagnostic, getPreviewDiagnosticRevision, subscribePreviewDiagnostics, } from "../changes/previewDiagnostics.js";
|
|
6
|
-
import { humanizeSelector } from "../tokens/catalog.js";
|
|
7
|
-
import { getScopingSelectorPattern } from "../runtime/runtimeConfig.js";
|
|
8
6
|
import { isVerificationPending } from "./staleChangeDetector.js";
|
|
9
7
|
export function StaleChangeIndicator({ change }) {
|
|
10
8
|
useSyncExternalStore(subscribePreviewDiagnostics, getPreviewDiagnosticRevision, getPreviewDiagnosticRevision);
|
|
@@ -21,23 +19,15 @@ export function StaleChangeIndicator({ change }) {
|
|
|
21
19
|
return (_jsx("span", { className: "changes__diagnostic", "data-test": "preview-applied", "data-status": "applied", children: "Preview active" }));
|
|
22
20
|
}
|
|
23
21
|
if (result.reason === "target-missing") {
|
|
24
|
-
|
|
25
|
-
// surface as human-facing guidance; the raw selector stays in change
|
|
26
|
-
// records for managed-rule targeting.
|
|
27
|
-
const selectorLabel = humanizeSelector(change.selector, getScopingSelectorPattern())
|
|
28
|
-
|| change.selector;
|
|
29
|
-
return (_jsx("span", { className: "changes__stale", "data-test": "stale-missing", title: `Selector: ${selectorLabel}`, children: "Source missing \u2014 this edit may be stale" }));
|
|
22
|
+
return (_jsx("span", { className: "changes__stale", "data-test": "stale-missing", children: "Source missing \u2014 this edit may be stale" }));
|
|
30
23
|
}
|
|
31
24
|
if (result.reason === "token-drift") {
|
|
32
|
-
const currentValue = result.computedValue
|
|
33
|
-
? ` (current: ${result.computedValue})`
|
|
34
|
-
: "";
|
|
35
25
|
if (isTokenChange(change)) {
|
|
36
|
-
return (_jsx("span", { className: "changes__stale", "data-test": "stale-token-drift",
|
|
26
|
+
return (_jsx("span", { className: "changes__stale", "data-test": "stale-token-drift", children: "Token baseline has changed" }));
|
|
37
27
|
}
|
|
38
28
|
return null;
|
|
39
29
|
}
|
|
40
|
-
return (_jsxs("span", { className: "changes__conflict", "data-test": "preview-conflict",
|
|
30
|
+
return (_jsxs("span", { className: "changes__conflict", "data-test": "preview-conflict", children: ["Preview Blocked (", result.reason
|
|
41
31
|
? result.reason.replace(/-/g, " ")
|
|
42
32
|
: "Unknown", ")"] }));
|
|
43
33
|
}
|