nudge-ui 0.2.0 → 0.2.2
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/bin/nudge-ui.mjs +3 -1
- package/dist/cli/agentCommand.d.ts +8 -0
- package/dist/cli/agentCommand.js +59 -0
- package/dist/client.mjs +878 -11
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
- package/dist/hosts/astro/clientTransport.js +63 -11
- package/dist/hosts/astro/integration.d.ts +2 -2
- package/dist/hosts/astro/integration.js +4 -3
- package/dist/hosts/environment.d.ts +15 -0
- package/dist/hosts/environment.js +16 -0
- package/dist/hosts/next/sidecar.d.ts +4 -0
- package/dist/hosts/next/sidecar.js +56 -7
- package/dist/hosts/next/wrapper.d.ts +2 -0
- package/dist/hosts/next/wrapper.js +32 -3
- package/dist/hosts/projectBridge.d.ts +21 -0
- package/dist/hosts/projectBridge.js +98 -0
- package/dist/hosts/static/cli.d.ts +1 -1
- package/dist/hosts/static/cli.js +6 -1
- package/dist/hosts/static/manifest.d.ts +1 -1
- package/dist/hosts/static/manifest.js +3 -1
- package/dist/hosts/static/server.js +45 -3
- package/dist/hosts/vite/vite.d.ts +6 -1
- package/dist/hosts/vite/vite.js +82 -15
- package/dist/inspector/agent/McpConnectionDialog.d.ts +5 -4
- package/dist/inspector/agent/McpConnectionDialog.js +20 -37
- package/dist/inspector/agent/client.d.ts +16 -3
- package/dist/inspector/agent/client.js +114 -11
- package/dist/inspector/agent/connectionStatus.d.ts +3 -3
- package/dist/inspector/agent/connectionStatus.js +11 -4
- package/dist/inspector/agent/httpTransport.d.ts +8 -0
- package/dist/inspector/agent/httpTransport.js +78 -18
- package/dist/inspector/agent/protocol.d.ts +11 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +1 -1
- package/dist/inspector/agent/verification.js +69 -12
- package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
- package/dist/inspector/canvas/CanvasCard.js +181 -91
- package/dist/inspector/canvas/CanvasElementOverlay.js +288 -52
- package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
- package/dist/inspector/canvas/CanvasToolbar.js +24 -0
- package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +410 -72
- package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
- package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
- package/dist/inspector/canvas/canvasResize.d.ts +30 -0
- package/dist/inspector/canvas/canvasResize.js +52 -0
- package/dist/inspector/canvas/canvasStore.d.ts +30 -6
- package/dist/inspector/canvas/canvasStore.js +128 -61
- package/dist/inspector/canvas/frameProtocol.d.ts +82 -5
- package/dist/inspector/canvas/frameProtocol.js +63 -2
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -0
- package/dist/inspector/canvas/keyboardShortcuts.js +16 -0
- package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
- package/dist/inspector/canvas/linkEligibility.js +0 -29
- package/dist/inspector/canvas/projection.d.ts +7 -0
- package/dist/inspector/canvas/projection.js +118 -25
- package/dist/inspector/canvas/rendererBootstrap.js +42 -29
- package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
- package/dist/inspector/canvas/rendererElementSelector.js +385 -58
- package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
- package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
- package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
- package/dist/inspector/canvas/sessionStore.d.ts +1 -0
- package/dist/inspector/canvas/sessionStore.js +12 -4
- package/dist/inspector/canvas/staleChangeDetector.js +16 -14
- package/dist/inspector/changes/changesLog.js +31 -1
- package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
- package/dist/inspector/changes/managedStyleProjection.js +3 -3
- package/dist/inspector/changes/structuralTypes.d.ts +4 -0
- package/dist/inspector/changes/workspaceChanges.d.ts +4 -0
- package/dist/inspector/changes/workspaceChanges.js +29 -3
- package/dist/inspector/clientManifest.d.ts +7 -0
- package/dist/inspector/clientManifest.js +12 -0
- package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
- package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
- package/dist/inspector/index.d.ts +2 -0
- package/dist/inspector/index.js +80 -32
- package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
- package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
- package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
- package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
- package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
- package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
- package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
- package/dist/inspector/inline-text/textEditChange.js +165 -0
- package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
- package/dist/inspector/inspection/selectionProperty.js +1 -0
- package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
- package/dist/inspector/overlay/DomNavigator.js +40 -0
- package/dist/inspector/overlay/InspectorOverlay.js +6 -2
- package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
- package/dist/inspector/overlay/clickPolicy.js +34 -3
- package/dist/inspector/overlay/elementSelector.d.ts +1 -1
- package/dist/inspector/overlay/elementSelector.js +18 -7
- package/dist/inspector/overlay/selectedGeometry.js +21 -2
- package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
- package/dist/inspector/overlay/spacingGestures.js +351 -0
- package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
- package/dist/inspector/overlay/structuralGestures.js +12 -10
- package/dist/inspector/projection/managedStylesheet.js +4 -1
- package/dist/inspector/projection/renderedInstance.d.ts +2 -0
- package/dist/inspector/projection/renderedInstance.js +5 -1
- package/dist/inspector/projection/structuralProjection.d.ts +2 -0
- package/dist/inspector/projection/structuralProjection.js +85 -1
- package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
- package/dist/inspector/projection/textProjection.d.ts +2 -0
- package/dist/inspector/projection/textProjection.js +10 -4
- package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
- package/dist/inspector/prompt/clipboardHandoff.js +3 -3
- package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
- package/dist/inspector/prompt/generatePrompt.js +19 -2
- package/dist/inspector/runtime/editorShell.d.ts +2 -0
- package/dist/inspector/runtime/editorShell.js +4 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +5 -1
- package/dist/inspector/runtime/runtimeConfig.js +4 -0
- package/dist/inspector/selection/hierarchy.d.ts +6 -0
- package/dist/inspector/selection/hierarchy.js +8 -0
- package/dist/inspector/selection/selectionStore.js +2 -0
- package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
- package/dist/inspector/settings/SettingsDialog.js +12 -2
- package/dist/inspector/shell/AppShell.js +5 -1
- package/dist/inspector/shell/ChangesLog.js +26 -22
- package/dist/inspector/shell/CopyPromptButton.js +102 -19
- package/dist/inspector/shell/EmptyState.js +1 -1
- package/dist/inspector/shell/InspectorShell.d.ts +0 -1
- package/dist/inspector/shell/InspectorShell.js +38 -42
- package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
- package/dist/inspector/shell/PeekOriginalButton.js +57 -0
- package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
- package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
- package/dist/inspector/shell/originalPreview.d.ts +9 -0
- package/dist/inspector/shell/originalPreview.js +61 -0
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +15 -0
- package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
- package/dist/inspector/sketch/SketchChanges.js +72 -0
- package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
- package/dist/inspector/sketch/SketchEditor.js +402 -0
- package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
- package/dist/inspector/sketch/SketchEntryButton.js +11 -0
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
- package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
- package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
- package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
- package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
- package/dist/inspector/sketch/SketchOverlay.js +420 -0
- package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
- package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
- package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
- package/dist/inspector/sketch/SketchWorkspace.js +152 -0
- package/dist/inspector/sketch/capture.d.ts +55 -0
- package/dist/inspector/sketch/capture.js +350 -0
- package/dist/inspector/sketch/freehand.d.ts +13 -0
- package/dist/inspector/sketch/freehand.js +64 -0
- package/dist/inspector/sketch/geometry.d.ts +17 -0
- package/dist/inspector/sketch/geometry.js +98 -0
- package/dist/inspector/sketch/handoff.d.ts +21 -0
- package/dist/inspector/sketch/handoff.js +72 -0
- package/dist/inspector/sketch/interaction.d.ts +21 -0
- package/dist/inspector/sketch/interaction.js +98 -0
- package/dist/inspector/sketch/model.d.ts +104 -0
- package/dist/inspector/sketch/model.js +282 -0
- package/dist/inspector/sketch/persistence.d.ts +21 -0
- package/dist/inspector/sketch/persistence.js +205 -0
- package/dist/inspector/sketch/raster.d.ts +22 -0
- package/dist/inspector/sketch/raster.js +203 -0
- package/dist/inspector/sketch/sketchNote.d.ts +10 -0
- package/dist/inspector/sketch/sketchNote.js +30 -0
- package/dist/inspector/sketch/store.d.ts +47 -0
- package/dist/inspector/sketch/store.js +434 -0
- package/dist/inspector/styleEditors/BorderEditor.js +15 -19
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
- package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
- package/dist/inspector/styleEditors/ColorPicker.js +11 -36
- package/dist/inspector/styleEditors/GapField.d.ts +3 -2
- package/dist/inspector/styleEditors/GapField.js +2 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/LayoutSection.js +11 -8
- package/dist/inspector/styleEditors/SpacingBox.js +31 -33
- package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
- package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
- package/dist/inspector/styleEditors/layoutValue.js +3 -0
- package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
- package/dist/inspector/styleEditors/nudgeValue.js +93 -5
- package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
- package/dist/inspector/styleEditors/stylePresence.js +22 -0
- package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
- package/dist/inspector/styleEditors/textRelevance.js +35 -0
- package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
- package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
- package/dist/inspector/tokens/TokenField.js +130 -7
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
- package/dist/inspector/tokens/dragPointerLock.js +70 -0
- package/dist/inspector/tokens/editActions.d.ts +2 -0
- package/dist/inspector/tokens/editActions.js +2 -2
- package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
- package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
- package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
- package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
- package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
- package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
- package/dist/inspector/tokens/resolution.d.ts +3 -26
- package/dist/inspector/tokens/resolution.js +44 -735
- package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
- package/dist/inspector/tokens/tokenAvailability.js +204 -0
- package/dist/inspector/ui/Button.d.ts +1 -1
- package/dist/inspector/ui/Disclosure.d.ts +22 -0
- package/dist/inspector/ui/Disclosure.js +13 -0
- package/dist/inspector/ui/SideValuesField.js +2 -2
- package/dist/inspector/ui/StatusCallout.d.ts +4 -1
- package/dist/inspector/ui/StatusCallout.js +2 -2
- package/dist/inspector/ui/dragCursor.d.ts +8 -0
- package/dist/inspector/ui/dragCursor.js +21 -0
- package/dist/inspector/ui/styles.js +21 -17
- package/dist/internal/agent-protocol.d.ts +65 -1
- package/dist/internal/agent-protocol.js +183 -3
- package/dist/nudge-ui.mjs +276 -58
- package/dist/transport/editor.d.ts +47 -0
- package/dist/transport/editor.js +163 -0
- package/dist/transport/index.d.ts +1 -0
- package/dist/transport/index.js +1 -0
- package/dist/transport/routes.d.ts +10 -0
- package/dist/transport/routes.js +10 -0
- package/package.json +3 -1
- package/dist/inspector/shell/DomNavigation.d.ts +0 -8
- package/dist/inspector/shell/DomNavigation.js +0 -30
- package/dist/inspector/shell/panelLayout.d.ts +0 -2
- package/dist/inspector/shell/panelLayout.js +0 -29
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
3
|
-
import { isElementClickMessage,
|
|
4
|
-
import {
|
|
5
|
-
import { useBoardCamera, useCanvasCards } from "./canvasStore.js";
|
|
2
|
+
import { useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
3
|
+
import { PROTOCOL_VERSION, isElementClickMessage, isInlineTextIntentMessage, } from "./frameProtocol.js";
|
|
4
|
+
import { useBoardCamera, useCanvasCards, useCanvasPresentation, useCanvasPresentationTransitioning } from "./canvasStore.js";
|
|
6
5
|
import { handleElementClick } from "./rendererSelectionProxy.js";
|
|
7
|
-
import { getSelectedElements, useSelectedElement, useSelectedElements } from "../selection/selectionStore.js";
|
|
8
|
-
import {
|
|
9
|
-
const overlayStyles = ".canvas-element-overlay {\n position: fixed;\n pointer-events: none;\n z-index:
|
|
6
|
+
import { getSelectedElements, useHierarchy, useSelectedElement, useSelectedElements } from "../selection/selectionStore.js";
|
|
7
|
+
import { readBorderWidths, toRect, } from "../overlay/overlayGeometry.js";
|
|
8
|
+
const overlayStyles = ".canvas-element-overlay {\n position: fixed;\n pointer-events: none;\n z-index: 1;\n background: transparent;\n outline: 1px solid var(--accent);\n outline-offset: -1px;\n}\n\n.canvas-spacing-guide {\n position: fixed;\n z-index: 1;\n border-radius: 999px;\n background: var(--accent);\n outline: none;\n box-shadow: none;\n pointer-events: none;\n}\n\n.canvas-spacing-fill {\n position: fixed;\n z-index: 1;\n pointer-events: none;\n background: rgb(59 130 246 / 14%);\n}\n\n.canvas-spacing-fill[data-kind=\"gap\"] {\n background: rgb(236 72 153 / 14%);\n}\n\n.canvas-spacing-guide[data-kind=\"gap\"] {\n background: #ec4899;\n}\n\n.canvas-selected-outline {\n position: fixed;\n pointer-events: none;\n z-index: 1;\n outline: 1px solid var(--accent);\n outline-offset: -1px;\n}\n\n.canvas-dom-drop-line {\n z-index: 2147483647;\n border-radius: 999px;\n background: #3b82f6;\n box-shadow: 0 0 0 1px rgb(255 255 255 / 95%), 0 1px 5px rgb(37 99 235 / 72%);\n}\n\n.canvas-dom-drop-line::before,\n.canvas-dom-drop-line::after {\n position: absolute;\n top: 50%;\n width: 8px;\n height: 8px;\n border: 2px solid #ffffff;\n border-radius: 50%;\n background: #2563eb;\n content: \"\";\n transform: translateY(-50%);\n}\n\n.canvas-dom-drop-line::before { left: -3px; }\n.canvas-dom-drop-line::after { right: -3px; }\n\n.canvas-dom-drop-line[data-orientation=\"vertical\"]::before,\n.canvas-dom-drop-line[data-orientation=\"vertical\"]::after {\n left: 50%;\n right: auto;\n transform: translateX(-50%);\n}\n\n.canvas-dom-drop-line[data-orientation=\"vertical\"]::before { top: -3px; }\n.canvas-dom-drop-line[data-orientation=\"vertical\"]::after { top: auto; bottom: -3px; }\n\n.canvas-dom-drop-target {\n z-index: 2147483646;\n box-sizing: border-box;\n border: 1px solid rgb(59 130 246 / 72%);\n border-radius: 4px;\n background: rgb(59 130 246 / 5%);\n}\n";
|
|
10
9
|
import { deleteElement, getDropLocationAtPoint, moveElement, nudgeElement } from "../overlay/structuralGestures.js";
|
|
11
10
|
import { redo, undo } from "../changes/changesLog.js";
|
|
12
11
|
import { resolveSelectionFromElement } from "../selection/resolveSelection.js";
|
|
@@ -16,8 +15,18 @@ import { DropGuideOverlay } from "../overlay/DropGuideOverlay.js";
|
|
|
16
15
|
import { getMeasurementGeometry } from "../overlay/measurementGeometry.js";
|
|
17
16
|
import { MeasurementGuideOverlay } from "../overlay/MeasurementGuideOverlay.js";
|
|
18
17
|
import { projectMeasurementSegments } from "./measurementProjection.js";
|
|
18
|
+
import { getRegisteredFrames, PROJECT_ID, WORKSPACE_ID } from "./projection.js";
|
|
19
19
|
import { RENDERER_ELEMENT_ID_ATTR } from "./rendererCidIndex.js";
|
|
20
20
|
import { observeSelectedGeometry } from "../overlay/selectedGeometry.js";
|
|
21
|
+
import { handleInlineTextEditIntent, useInlineTextSession } from "../inline-text/inlineTextEditor.js";
|
|
22
|
+
import { setInspectorOpen, toggleInspector } from "../shell/openStore.js";
|
|
23
|
+
import { EMPTY_TEXT_PROJECTION_ATTR } from "../projection/textProjection.js";
|
|
24
|
+
import { subscribeCanvasRendererMessages } from "./rendererMessageRouter.js";
|
|
25
|
+
import { setStyles } from "../tokens/editActions.js";
|
|
26
|
+
import { getSpacingAffordanceAtPoint, getSpacingAffordanceForDescriptor, SPACING_GUIDE_HANDLE_LENGTH, spacingValueCss, spacingValueForDrag, } from "../overlay/spacingGestures.js";
|
|
27
|
+
import { beginLayoutPreview, endLayoutPreview } from "../styleEditors/layoutPreviewState.js";
|
|
28
|
+
import { useNudgeUiRuntimeConfig } from "../runtime/useRuntimeConfig.js";
|
|
29
|
+
import { DomNavigator } from "../overlay/DomNavigator.js";
|
|
21
30
|
function projectRect(iframe, rect, zoom) {
|
|
22
31
|
const frameRect = iframe.getBoundingClientRect();
|
|
23
32
|
return {
|
|
@@ -27,14 +36,6 @@ function projectRect(iframe, rect, zoom) {
|
|
|
27
36
|
height: rect.height * zoom,
|
|
28
37
|
};
|
|
29
38
|
}
|
|
30
|
-
function scaleMargins(margins, zoom) {
|
|
31
|
-
return {
|
|
32
|
-
top: margins.top * zoom,
|
|
33
|
-
right: margins.right * zoom,
|
|
34
|
-
bottom: margins.bottom * zoom,
|
|
35
|
-
left: margins.left * zoom,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
39
|
function overlayStyle(rect) {
|
|
39
40
|
return {
|
|
40
41
|
position: "fixed",
|
|
@@ -45,6 +46,17 @@ function overlayStyle(rect) {
|
|
|
45
46
|
pointerEvents: "none",
|
|
46
47
|
};
|
|
47
48
|
}
|
|
49
|
+
function spacingGuideStyle(rect) {
|
|
50
|
+
const isHorizontal = rect.width >= rect.height;
|
|
51
|
+
const width = isHorizontal ? Math.min(rect.width, SPACING_GUIDE_HANDLE_LENGTH) : rect.width;
|
|
52
|
+
const height = isHorizontal ? rect.height : Math.min(rect.height, SPACING_GUIDE_HANDLE_LENGTH);
|
|
53
|
+
return overlayStyle({
|
|
54
|
+
left: rect.left + (rect.width - width) / 2,
|
|
55
|
+
top: rect.top + (rect.height - height) / 2,
|
|
56
|
+
width,
|
|
57
|
+
height,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
48
60
|
function findFrameElement(iframe, elementId, cid, src) {
|
|
49
61
|
const doc = iframe.contentDocument;
|
|
50
62
|
if (!doc)
|
|
@@ -55,7 +67,9 @@ function findFrameElement(iframe, elementId, cid, src) {
|
|
|
55
67
|
if (candidates.length !== 1)
|
|
56
68
|
return null;
|
|
57
69
|
const candidate = candidates[0] ?? null;
|
|
58
|
-
if (!candidate
|
|
70
|
+
if (!candidate
|
|
71
|
+
|| candidate.getAttribute("data-cid") !== cid
|
|
72
|
+
|| (candidate.getAttribute("data-src") ?? "") !== src)
|
|
59
73
|
return null;
|
|
60
74
|
const frameWindow = doc.defaultView;
|
|
61
75
|
return frameWindow && candidate instanceof frameWindow.HTMLElement ? candidate : null;
|
|
@@ -79,17 +93,79 @@ function projectGuideToCanvas(guide, zoom) {
|
|
|
79
93
|
target: project(guide.target, true, true),
|
|
80
94
|
};
|
|
81
95
|
}
|
|
96
|
+
function spacingDescriptorMatches(affordance, descriptor) {
|
|
97
|
+
return Boolean(affordance
|
|
98
|
+
&& descriptor
|
|
99
|
+
&& affordance.kind === descriptor.kind
|
|
100
|
+
&& affordance.property === descriptor.property
|
|
101
|
+
&& affordance.side === descriptor.side);
|
|
102
|
+
}
|
|
103
|
+
function clearSpacingDrag(ref) {
|
|
104
|
+
const drag = ref.current;
|
|
105
|
+
ref.current = null;
|
|
106
|
+
if (drag)
|
|
107
|
+
restoreSpacingPreview(drag);
|
|
108
|
+
}
|
|
109
|
+
function applySpacingPreview(drag, point, shiftKey = false) {
|
|
110
|
+
const value = spacingValueForDrag(drag.affordance, drag.start, point, shiftKey);
|
|
111
|
+
drag.currentValue = value;
|
|
112
|
+
beginLayoutPreview(drag.element, drag.affordance.property);
|
|
113
|
+
drag.element.style.setProperty(drag.affordance.property, spacingValueCss(value));
|
|
114
|
+
}
|
|
115
|
+
function restoreSpacingPreview(drag) {
|
|
116
|
+
if (drag.originalInlineValue) {
|
|
117
|
+
drag.element.style.setProperty(drag.affordance.property, drag.originalInlineValue, drag.originalInlinePriority);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
drag.element.style.removeProperty(drag.affordance.property);
|
|
121
|
+
}
|
|
122
|
+
endLayoutPreview(drag.element, drag.affordance.property);
|
|
123
|
+
}
|
|
124
|
+
function commitSpacingDrag(drag) {
|
|
125
|
+
// Restore the authored declaration before committing the managed rule. The
|
|
126
|
+
// captured start value is the baseline because the pointer remains hovered
|
|
127
|
+
// and CSS inspection can still report the transient preview value.
|
|
128
|
+
restoreSpacingPreview(drag);
|
|
129
|
+
if (drag.currentValue === drag.startValue)
|
|
130
|
+
return;
|
|
131
|
+
setStyles(drag.element, [{
|
|
132
|
+
property: drag.affordance.property,
|
|
133
|
+
value: spacingValueCss(drag.currentValue),
|
|
134
|
+
oldRawValue: spacingValueCss(drag.startValue),
|
|
135
|
+
}]);
|
|
136
|
+
}
|
|
137
|
+
function sendMeasureModifier(iframe, cardId, altKey) {
|
|
138
|
+
iframe.contentWindow?.postMessage({
|
|
139
|
+
type: "measure-modifier",
|
|
140
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
141
|
+
projectId: PROJECT_ID,
|
|
142
|
+
workspaceId: WORKSPACE_ID,
|
|
143
|
+
cardId,
|
|
144
|
+
altKey,
|
|
145
|
+
}, window.location.origin);
|
|
146
|
+
}
|
|
82
147
|
export function CanvasElementOverlay() {
|
|
83
148
|
const [hover, setHover] = useState(null);
|
|
84
149
|
const [measureState, setMeasureState] = useState(null);
|
|
85
150
|
const [, refreshSelectedGeometry] = useReducer((revision) => revision + 1, 0);
|
|
86
151
|
const selected = useSelectedElement();
|
|
87
152
|
const selectedElements = useSelectedElements();
|
|
153
|
+
const hierarchy = useHierarchy();
|
|
154
|
+
const domNavigationEnabled = useNudgeUiRuntimeConfig().capabilities.domNavigation === true;
|
|
155
|
+
const inlineTextSession = useInlineTextSession();
|
|
88
156
|
const camera = useBoardCamera();
|
|
89
|
-
|
|
157
|
+
const presentation = useCanvasPresentation();
|
|
158
|
+
const presentationTransitioning = useCanvasPresentationTransitioning();
|
|
159
|
+
const projectionZoom = presentation === "focus" ? 1 : camera.zoom;
|
|
160
|
+
const cards = useCanvasCards(); // Projected geometry must follow card drag and resize updates.
|
|
161
|
+
const cardGeometryKey = cards.map((card) => `${card.id}:${card.x}:${card.y}:${card.width}:${card.height}`).join("|");
|
|
162
|
+
const projectionGeometryKey = `${presentation}:${camera.x}:${camera.y}:${projectionZoom}:${cardGeometryKey}`;
|
|
163
|
+
const previousProjectionGeometryKey = useRef(projectionGeometryKey);
|
|
90
164
|
const dragRef = useRef(null);
|
|
165
|
+
const spacingDragRef = useRef(null);
|
|
166
|
+
const measureAltKeyRef = useRef(false);
|
|
91
167
|
const dropGuide = useDropGuide("canvas");
|
|
92
|
-
const projectedDropGuide = projectGuideToCanvas(dropGuide,
|
|
168
|
+
const projectedDropGuide = projectGuideToCanvas(dropGuide, projectionZoom);
|
|
93
169
|
const selectedFrame = selected?.domElement.ownerDocument.defaultView?.frameElement;
|
|
94
170
|
const selectedInCanvas = selectedFrame instanceof HTMLIFrameElement
|
|
95
171
|
&& selectedFrame.hasAttribute("data-nudge-ui-canvas-renderer");
|
|
@@ -105,16 +181,25 @@ export function CanvasElementOverlay() {
|
|
|
105
181
|
? toRect(selectedFrame.getBoundingClientRect())
|
|
106
182
|
: null;
|
|
107
183
|
const selectedRect = selectedInCanvas && selectedLocalRect
|
|
108
|
-
? projectRect(selectedFrame, selectedLocalRect,
|
|
184
|
+
? projectRect(selectedFrame, selectedLocalRect, projectionZoom)
|
|
109
185
|
: null;
|
|
110
186
|
const projectedSelectedGeometry = selectedInCanvas
|
|
111
187
|
? selectedRects.flatMap((rect, index) => {
|
|
112
188
|
const element = selectedElements[index];
|
|
113
189
|
return rect && element
|
|
114
|
-
? [{ element, rect: projectRect(selectedFrame, rect,
|
|
190
|
+
? [{ element, rect: projectRect(selectedFrame, rect, projectionZoom) }]
|
|
115
191
|
: [];
|
|
116
192
|
})
|
|
117
193
|
: [];
|
|
194
|
+
// Camera transforms commit on the board before iframe viewport geometry is
|
|
195
|
+
// readable. Re-render once after that commit so overlays use the transformed
|
|
196
|
+
// frame rectangle rather than the previous camera position.
|
|
197
|
+
useLayoutEffect(() => {
|
|
198
|
+
const geometryChanged = previousProjectionGeometryKey.current !== projectionGeometryKey;
|
|
199
|
+
previousProjectionGeometryKey.current = projectionGeometryKey;
|
|
200
|
+
if (selectedInCanvas && geometryChanged)
|
|
201
|
+
refreshSelectedGeometry();
|
|
202
|
+
}, [projectionGeometryKey, selectedInCanvas]);
|
|
118
203
|
const selectedBorders = selectedInCanvas && selected
|
|
119
204
|
? readBorderWidths(selected.domElement)
|
|
120
205
|
: null;
|
|
@@ -124,6 +209,32 @@ export function CanvasElementOverlay() {
|
|
|
124
209
|
const stops = selectedElements.map((candidate) => observeSelectedGeometry(candidate.domElement, refreshSelectedGeometry));
|
|
125
210
|
return () => stops.forEach((stop) => stop());
|
|
126
211
|
}, [selected, selectedElements, selectedInCanvas]);
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
const broadcastMeasureModifier = (altKey) => {
|
|
214
|
+
measureAltKeyRef.current = altKey;
|
|
215
|
+
for (const [cardId, iframe] of getRegisteredFrames()) {
|
|
216
|
+
sendMeasureModifier(iframe, cardId, altKey);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const onKeyDown = (event) => {
|
|
220
|
+
if (event.key !== "Alt" || measureAltKeyRef.current)
|
|
221
|
+
return;
|
|
222
|
+
broadcastMeasureModifier(true);
|
|
223
|
+
};
|
|
224
|
+
const onKeyUp = (event) => {
|
|
225
|
+
if (event.key === "Alt")
|
|
226
|
+
broadcastMeasureModifier(false);
|
|
227
|
+
};
|
|
228
|
+
const onBlur = () => broadcastMeasureModifier(false);
|
|
229
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
230
|
+
window.addEventListener("keyup", onKeyUp, true);
|
|
231
|
+
window.addEventListener("blur", onBlur);
|
|
232
|
+
return () => {
|
|
233
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
234
|
+
window.removeEventListener("keyup", onKeyUp, true);
|
|
235
|
+
window.removeEventListener("blur", onBlur);
|
|
236
|
+
};
|
|
237
|
+
}, []);
|
|
127
238
|
// The selected element's identity is computed once per selection change
|
|
128
239
|
// (never per hover), so the measurement self-rulers exclusion can compare
|
|
129
240
|
// identities without a per-hover scan of the frame document.
|
|
@@ -135,24 +246,41 @@ export function CanvasElementOverlay() {
|
|
|
135
246
|
return elementId ? { elementId } : null;
|
|
136
247
|
}, [selected?.domElement]);
|
|
137
248
|
useEffect(() => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const sourceFrame = findCanvasFrameBySource(event.source);
|
|
144
|
-
if (!sourceFrame)
|
|
145
|
-
return;
|
|
146
|
-
const { cardId: sourceCardId, iframe: sourceIframe } = sourceFrame;
|
|
147
|
-
const frameIdentity = {
|
|
148
|
-
projectId: PROJECT_ID,
|
|
149
|
-
workspaceId: WORKSPACE_ID,
|
|
150
|
-
cardId: sourceCardId,
|
|
249
|
+
const refreshSpacingPreviewOverlay = (drag) => {
|
|
250
|
+
const descriptor = {
|
|
251
|
+
kind: drag.affordance.kind,
|
|
252
|
+
property: drag.affordance.property,
|
|
253
|
+
side: drag.affordance.side,
|
|
151
254
|
};
|
|
152
|
-
|
|
255
|
+
const affordance = getSpacingAffordanceForDescriptor(drag.element, descriptor);
|
|
256
|
+
const rect = toRect(drag.element.getBoundingClientRect());
|
|
257
|
+
const borders = readBorderWidths(drag.element);
|
|
258
|
+
const elementId = drag.element.getAttribute(RENDERER_ELEMENT_ID_ATTR);
|
|
259
|
+
setHover((current) => {
|
|
260
|
+
if (!current
|
|
261
|
+
|| current.iframe !== drag.iframe
|
|
262
|
+
|| current.identity.elementId !== elementId
|
|
263
|
+
|| !current.spacing
|
|
264
|
+
|| current.spacing.kind !== descriptor.kind
|
|
265
|
+
|| current.spacing.property !== descriptor.property
|
|
266
|
+
|| current.spacing.side !== descriptor.side) {
|
|
267
|
+
return current;
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
...current,
|
|
271
|
+
rect,
|
|
272
|
+
borders,
|
|
273
|
+
spacingGuides: affordance?.affectedGuides ?? [],
|
|
274
|
+
spacingAreas: affordance?.affectedAreas ?? [],
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
};
|
|
278
|
+
const unsubscribe = subscribeCanvasRendererMessages(({ cardId: sourceCardId, iframe: sourceIframe, identity: frameIdentity, message: data, }) => {
|
|
279
|
+
if (data.type === "renderer-hello" || data.type === "frame-ready") {
|
|
280
|
+
sendMeasureModifier(sourceIframe, sourceCardId, measureAltKeyRef.current);
|
|
281
|
+
}
|
|
282
|
+
if (data.type === "renderer-hello")
|
|
153
283
|
return;
|
|
154
|
-
// SAFETY: isRendererMessageFor validated the frame identity and message shape above.
|
|
155
|
-
const data = event.data;
|
|
156
284
|
if (data.type === "element-hover") {
|
|
157
285
|
const msg = data;
|
|
158
286
|
if (!msg.cid)
|
|
@@ -161,13 +289,30 @@ export function CanvasElementOverlay() {
|
|
|
161
289
|
setHover((current) => current?.cardId === sourceCardId ? null : current);
|
|
162
290
|
return;
|
|
163
291
|
}
|
|
292
|
+
const spacingDescriptor = msg.spacing ?? null;
|
|
293
|
+
const hoverPoint = msg.point ?? null;
|
|
294
|
+
const hoverElement = spacingDescriptor
|
|
295
|
+
? findFrameElement(sourceIframe, msg.elementId, msg.cid, msg.src)
|
|
296
|
+
: null;
|
|
297
|
+
const hoverAffordance = spacingDescriptor && hoverElement
|
|
298
|
+
? getSpacingAffordanceForDescriptor(hoverElement, spacingDescriptor)
|
|
299
|
+
: spacingDescriptor && hoverPoint && sourceIframe.contentDocument
|
|
300
|
+
? getSpacingAffordanceAtPoint(sourceIframe.contentDocument, hoverPoint.x, hoverPoint.y)
|
|
301
|
+
: null;
|
|
302
|
+
const matchingAffordance = spacingDescriptorMatches(hoverAffordance, spacingDescriptor)
|
|
303
|
+
? hoverAffordance
|
|
304
|
+
: null;
|
|
305
|
+
const spacingGuides = matchingAffordance?.affectedGuides ?? [];
|
|
306
|
+
const spacingAreas = matchingAffordance?.affectedAreas ?? [];
|
|
164
307
|
setHover({
|
|
165
308
|
iframe: sourceIframe,
|
|
166
309
|
identity: { elementId: msg.elementId },
|
|
167
310
|
rect: msg.rect,
|
|
168
|
-
margins: msg.margins ?? { top: 0, right: 0, bottom: 0, left: 0 },
|
|
169
311
|
borders: msg.borders ?? { top: 0, right: 0, bottom: 0, left: 0 },
|
|
170
312
|
cardId: sourceCardId,
|
|
313
|
+
spacing: spacingGuides.length > 0 ? spacingDescriptor : null,
|
|
314
|
+
spacingGuides,
|
|
315
|
+
spacingAreas,
|
|
171
316
|
});
|
|
172
317
|
}
|
|
173
318
|
else if (data.type === "element-measure-state") {
|
|
@@ -179,14 +324,38 @@ export function CanvasElementOverlay() {
|
|
|
179
324
|
});
|
|
180
325
|
}
|
|
181
326
|
else if (data.type === "element-click") {
|
|
182
|
-
if (!isElementClickMessage(
|
|
327
|
+
if (!isElementClickMessage(data, frameIdentity))
|
|
183
328
|
return;
|
|
184
329
|
const msg = data;
|
|
185
330
|
if (!msg.cid)
|
|
186
331
|
return;
|
|
187
332
|
handleElementClick(msg, sourceIframe, sourceCardId);
|
|
188
333
|
}
|
|
334
|
+
else if (data.type === "inline-text-intent") {
|
|
335
|
+
if (!isInlineTextIntentMessage(data, frameIdentity))
|
|
336
|
+
return;
|
|
337
|
+
if (getSelectedElements().length > 1)
|
|
338
|
+
return;
|
|
339
|
+
const emptyProjection = data.emptyProjectionId
|
|
340
|
+
? Array.from(sourceIframe.contentDocument?.querySelectorAll(`[${EMPTY_TEXT_PROJECTION_ATTR}]`) ?? [])
|
|
341
|
+
.find((candidate) => candidate.getAttribute(EMPTY_TEXT_PROJECTION_ATTR) === data.emptyProjectionId)
|
|
342
|
+
: null;
|
|
343
|
+
const element = emptyProjection ?? findFrameElement(sourceIframe, data.elementId, data.cid, data.src);
|
|
344
|
+
if (!element)
|
|
345
|
+
return;
|
|
346
|
+
handleInlineTextEditIntent(data.intent === "double-click"
|
|
347
|
+
? { kind: "double-click", target: element, point: data.point }
|
|
348
|
+
: {
|
|
349
|
+
kind: "pointer-down",
|
|
350
|
+
target: element,
|
|
351
|
+
point: data.point,
|
|
352
|
+
clickCount: data.clickCount ?? 0,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
189
355
|
else if (data.type === "element-deselect") {
|
|
356
|
+
clearSpacingDrag(spacingDragRef);
|
|
357
|
+
dragRef.current = null;
|
|
358
|
+
clearDropGuide("canvas");
|
|
190
359
|
setSelectedElement(null);
|
|
191
360
|
}
|
|
192
361
|
else if (data.type === "element-drag-start") {
|
|
@@ -196,6 +365,35 @@ export function CanvasElementOverlay() {
|
|
|
196
365
|
const element = findFrameElement(sourceIframe, msg.elementId, msg.cid, msg.src);
|
|
197
366
|
if (!element)
|
|
198
367
|
return;
|
|
368
|
+
clearSpacingDrag(spacingDragRef);
|
|
369
|
+
dragRef.current = null;
|
|
370
|
+
const startPoint = msg.startPoint ?? msg.point;
|
|
371
|
+
const spacing = msg.spacing
|
|
372
|
+
? getSpacingAffordanceForDescriptor(element, msg.spacing)
|
|
373
|
+
: sourceIframe.contentDocument
|
|
374
|
+
? getSpacingAffordanceAtPoint(sourceIframe.contentDocument, startPoint.x, startPoint.y)
|
|
375
|
+
: null;
|
|
376
|
+
const isSpacingDrag = spacing
|
|
377
|
+
&& spacing.element === element
|
|
378
|
+
&& (!msg.spacing || spacingDescriptorMatches(spacing, msg.spacing));
|
|
379
|
+
if (isSpacingDrag) {
|
|
380
|
+
spacingDragRef.current = {
|
|
381
|
+
iframe: sourceIframe,
|
|
382
|
+
element,
|
|
383
|
+
affordance: spacing,
|
|
384
|
+
start: startPoint,
|
|
385
|
+
startValue: spacing.value,
|
|
386
|
+
currentValue: spacing.value,
|
|
387
|
+
originalInlineValue: element.style.getPropertyValue(spacing.property),
|
|
388
|
+
originalInlinePriority: element.style.getPropertyPriority(spacing.property),
|
|
389
|
+
};
|
|
390
|
+
const selectedElement = resolveSelectionFromElement(element);
|
|
391
|
+
if (selectedElement)
|
|
392
|
+
setSelectedElement(selectedElement);
|
|
393
|
+
applySpacingPreview(spacingDragRef.current, msg.point, msg.shiftKey === true);
|
|
394
|
+
refreshSpacingPreviewOverlay(spacingDragRef.current);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
199
397
|
dragRef.current = { iframe: sourceIframe, element };
|
|
200
398
|
const selectedElement = resolveSelectionFromElement(element);
|
|
201
399
|
if (selectedElement)
|
|
@@ -203,12 +401,38 @@ export function CanvasElementOverlay() {
|
|
|
203
401
|
updateDropGuide(msg.point, sourceIframe);
|
|
204
402
|
}
|
|
205
403
|
else if (data.type === "element-drag-move") {
|
|
404
|
+
const spacing = spacingDragRef.current;
|
|
405
|
+
if (spacing && spacing.iframe === sourceIframe) {
|
|
406
|
+
applySpacingPreview(spacing, data.point, data.shiftKey === true);
|
|
407
|
+
refreshSpacingPreviewOverlay(spacing);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
206
410
|
updateDropGuide(data.point, sourceIframe);
|
|
207
411
|
}
|
|
208
412
|
else if (data.type === "element-drag-end") {
|
|
209
413
|
const msg = data;
|
|
414
|
+
const spacing = spacingDragRef.current;
|
|
415
|
+
if (spacing && spacing.iframe === sourceIframe) {
|
|
416
|
+
spacingDragRef.current = null;
|
|
417
|
+
if (msg.cancelled) {
|
|
418
|
+
restoreSpacingPreview(spacing);
|
|
419
|
+
refreshSpacingPreviewOverlay(spacing);
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
applySpacingPreview(spacing, msg.point, msg.shiftKey === true);
|
|
423
|
+
commitSpacingDrag(spacing);
|
|
424
|
+
refreshSpacingPreviewOverlay(spacing);
|
|
425
|
+
}
|
|
426
|
+
clearDropGuide("canvas");
|
|
427
|
+
if (!msg.cancelled) {
|
|
428
|
+
const selectedElement = resolveSelectionFromElement(spacing.element);
|
|
429
|
+
if (selectedElement)
|
|
430
|
+
setSelectedElement(selectedElement);
|
|
431
|
+
}
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
210
434
|
const current = dragRef.current;
|
|
211
|
-
if (current && current.iframe === sourceIframe && sourceIframe.contentDocument) {
|
|
435
|
+
if (!msg.cancelled && current && current.iframe === sourceIframe && sourceIframe.contentDocument) {
|
|
212
436
|
const drop = getDropLocationAtPoint(sourceIframe.contentDocument, current.element, msg.point.x, msg.point.y);
|
|
213
437
|
if (drop)
|
|
214
438
|
moveElement(current.element, drop);
|
|
@@ -249,6 +473,12 @@ export function CanvasElementOverlay() {
|
|
|
249
473
|
undo();
|
|
250
474
|
}
|
|
251
475
|
}
|
|
476
|
+
else if (data.type === "inspector-toggle-request") {
|
|
477
|
+
toggleInspector();
|
|
478
|
+
}
|
|
479
|
+
else if (data.type === "inspector-open-request") {
|
|
480
|
+
setInspectorOpen(true);
|
|
481
|
+
}
|
|
252
482
|
function updateDropGuide(point, iframe) {
|
|
253
483
|
const current = dragRef.current;
|
|
254
484
|
if (!current || current.iframe !== iframe || !iframe.contentDocument)
|
|
@@ -260,23 +490,22 @@ export function CanvasElementOverlay() {
|
|
|
260
490
|
}
|
|
261
491
|
showDropGuide("canvas", iframe.contentDocument, drop);
|
|
262
492
|
}
|
|
263
|
-
}
|
|
264
|
-
window.addEventListener("message", onMessage);
|
|
493
|
+
});
|
|
265
494
|
return () => {
|
|
266
|
-
|
|
495
|
+
unsubscribe();
|
|
496
|
+
clearSpacingDrag(spacingDragRef);
|
|
497
|
+
dragRef.current = null;
|
|
267
498
|
clearDropGuide("canvas");
|
|
268
499
|
};
|
|
269
500
|
}, []);
|
|
501
|
+
if (inlineTextSession && inlineTextSession.host.ownerDocument === selected?.domElement.ownerDocument)
|
|
502
|
+
return null;
|
|
270
503
|
if (!hover && projectedSelectedGeometry.length === 0 && !projectedDropGuide)
|
|
271
504
|
return null;
|
|
272
|
-
const projectedHoverRect = hover ? projectRect(hover.iframe, hover.rect,
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
: [];
|
|
277
|
-
const hoverMarginFills = projectedHoverRect && hoverMargins
|
|
278
|
-
? getMarginFills(projectedHoverRect, hoverMargins)
|
|
279
|
-
: [];
|
|
505
|
+
const projectedHoverRect = hover ? projectRect(hover.iframe, hover.rect, projectionZoom) : null;
|
|
506
|
+
const projectedSpacingGuides = hover?.spacingGuides.map((guide) => projectRect(hover.iframe, guide, projectionZoom)) ?? [];
|
|
507
|
+
const projectedSpacingAreas = hover?.spacingAreas.map((area) => projectRect(hover.iframe, area, projectionZoom)) ?? [];
|
|
508
|
+
const hoverSpacing = hover?.spacing ?? null;
|
|
280
509
|
const measureStateForSelectedFrame = measureState?.iframe === selectedFrame ? measureState : null;
|
|
281
510
|
const hoverInSelectedFrame = hover?.iframe === selectedFrame ? hover : null;
|
|
282
511
|
const showGuideOverlay = Boolean(selectedElements.length === 1
|
|
@@ -287,6 +516,9 @@ export function CanvasElementOverlay() {
|
|
|
287
516
|
const isSelfHover = Boolean(hoverInSelectedFrame
|
|
288
517
|
&& selectedIdentity
|
|
289
518
|
&& hoverInSelectedFrame.identity.elementId === selectedIdentity.elementId);
|
|
519
|
+
// Selected elements already have a live geometry outline. The hover outline
|
|
520
|
+
// is based on a cached renderer message and can lag during spacing previews.
|
|
521
|
+
const showHoverOutline = Boolean(projectedHoverRect && !isSelfHover);
|
|
290
522
|
const showMeasurement = Boolean(showGuideOverlay
|
|
291
523
|
&& selectedLocalRect
|
|
292
524
|
&& hoverInSelectedFrame
|
|
@@ -295,7 +527,11 @@ export function CanvasElementOverlay() {
|
|
|
295
527
|
? projectMeasurementSegments(selectedFrameRect, getMeasurementGeometry(selectedLocalRect, hoverInSelectedFrame.rect, {
|
|
296
528
|
selectedBorders: selectedBorders ?? undefined,
|
|
297
529
|
hoveredBorders: hoverInSelectedFrame.borders,
|
|
298
|
-
}).segments,
|
|
530
|
+
}).segments, projectionZoom)
|
|
299
531
|
: [];
|
|
300
|
-
|
|
532
|
+
if (presentationTransitioning)
|
|
533
|
+
return _jsx("style", { "data-test": "canvas-element-overlay-styles", children: overlayStyles });
|
|
534
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { "data-test": "canvas-element-overlay-styles", children: overlayStyles }), showGuideOverlay && selectedRect && selectedFrameRect ? (_jsx(MeasurementGuideOverlay, { testId: "canvas-measurement-overlay", selectedRect: selectedRect, guideViewport: selectedFrameRect, segments: measurementSegments })) : null, projectedHoverRect ? (_jsxs(_Fragment, { children: [hoverSpacing ? projectedSpacingAreas.map((area, index) => (_jsx("div", { className: "canvas-spacing-fill", "data-test": "canvas-spacing-fill", "data-kind": hoverSpacing.kind, "data-property": hoverSpacing.property, "data-side": hoverSpacing.side ?? undefined, "data-area-index": index, style: overlayStyle(area), "aria-hidden": "true" }, `${hoverSpacing.property}-area-${index}`))) : null, showHoverOutline ? (_jsx("div", { className: "canvas-element-overlay", "data-test": "canvas-hover-outline", style: overlayStyle(projectedHoverRect), "aria-hidden": "true" })) : null, hoverSpacing ? projectedSpacingGuides.map((guide, index) => (_jsx("div", { className: "canvas-spacing-guide", "data-test": "canvas-spacing-guide", "data-kind": hoverSpacing.kind, "data-property": hoverSpacing.property, "data-side": hoverSpacing.side ?? undefined, "data-guide-index": index, style: spacingGuideStyle(guide), "aria-hidden": "true" }, `${hoverSpacing.property}-${index}`))) : null] })) : null, projectedSelectedGeometry.map(({ element, rect }, index) => (_jsx("div", { className: "canvas-selected-outline", "data-test": "canvas-selected-outline", "data-selected-cid": element.cid, "data-selected-index": index, "data-selected-src": element.src, style: overlayStyle(rect), "aria-hidden": "true" }, `${element.cid}-${index}`))), domNavigationEnabled && !inlineTextSession && selectedElements.length === 1 && selected && selectedRect && selectedFrame instanceof HTMLIFrameElement ? (_jsx(DomNavigator, { selected: selected, hierarchy: hierarchy, anchor: selectedRect, project: (element) => element.ownerDocument === selected.domElement.ownerDocument
|
|
535
|
+
? projectRect(selectedFrame, toRect(element.getBoundingClientRect()), projectionZoom)
|
|
536
|
+
: null })) : null, _jsx(DropGuideOverlay, { guide: projectedDropGuide, lineClassName: "canvas-dom-drop-line", lineTestId: "canvas-dom-drop-line", targetClassName: "canvas-dom-drop-target", targetTestId: "canvas-dom-drop-target" })] }));
|
|
301
537
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import type { CanvasInteractionTool } from "./keyboardShortcuts.js";
|
|
3
|
+
export type { CanvasInteractionTool } from "./keyboardShortcuts.js";
|
|
4
|
+
export declare const CANVAS_ZOOM_LEVELS: readonly [0.25, 0.5, 0.9, 1];
|
|
5
|
+
export declare function closestCanvasZoomLevel(zoom: number): number;
|
|
6
|
+
export declare function adjacentCanvasZoomLevel(zoom: number, direction: -1 | 1): number | null;
|
|
7
|
+
export interface CanvasToolbarProps {
|
|
8
|
+
readonly tool: CanvasInteractionTool;
|
|
9
|
+
readonly sketchEnabled: boolean;
|
|
10
|
+
readonly onToolChange: (tool: CanvasInteractionTool) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function CanvasToolbar({ tool, sketchEnabled, onToolChange, }: CanvasToolbarProps): ReactElement;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { IconHandStop, IconPlayerPlay, IconPointer2, IconSketching, } from "@tabler/icons-react";
|
|
3
|
+
import { IconButton } from "../ui/IconButton.js";
|
|
4
|
+
export const CANVAS_ZOOM_LEVELS = [0.25, 0.5, 0.9, 1];
|
|
5
|
+
export function closestCanvasZoomLevel(zoom) {
|
|
6
|
+
let closest = CANVAS_ZOOM_LEVELS[0];
|
|
7
|
+
for (const candidate of CANVAS_ZOOM_LEVELS) {
|
|
8
|
+
if (Math.abs(candidate - zoom) < Math.abs(closest - zoom))
|
|
9
|
+
closest = candidate;
|
|
10
|
+
}
|
|
11
|
+
return closest;
|
|
12
|
+
}
|
|
13
|
+
export function adjacentCanvasZoomLevel(zoom, direction) {
|
|
14
|
+
const current = closestCanvasZoomLevel(zoom);
|
|
15
|
+
const index = CANVAS_ZOOM_LEVELS.findIndex((candidate) => candidate === current);
|
|
16
|
+
const next = CANVAS_ZOOM_LEVELS[index + direction];
|
|
17
|
+
return next === undefined ? null : next;
|
|
18
|
+
}
|
|
19
|
+
function ToolButton({ active, disabled, label, description, icon, onClick, }) {
|
|
20
|
+
return (_jsx(IconButton, { variant: "quiet", size: "large", className: "canvas-toolbar__tool", label: label, title: description ?? label, "aria-pressed": active, "data-test": `canvas-tool-${label.toLowerCase()}`, "data-active": active ? "true" : "false", disabled: disabled, onClick: onClick, children: icon }));
|
|
21
|
+
}
|
|
22
|
+
export function CanvasToolbar({ tool, sketchEnabled, onToolChange, }) {
|
|
23
|
+
return (_jsxs("div", { className: "canvas-toolbar", "data-test": "canvas-toolbar", role: "toolbar", "aria-label": "Canvas tools", children: [_jsxs("div", { className: "canvas-toolbar__tools", role: "group", "aria-label": "Interaction tools", children: [_jsx(ToolButton, { active: tool === "design", label: "Design", description: "Design (V): click elements to inspect and edit them", icon: _jsx(IconPointer2, { size: "var(--icon-size-large)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), onClick: () => onToolChange("design") }), _jsx(ToolButton, { active: tool === "pan", label: "Pan", icon: _jsx(IconHandStop, { size: "var(--icon-size-large)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), onClick: () => onToolChange("pan") }), _jsx(ToolButton, { active: tool === "sketch", label: "Sketch", disabled: !sketchEnabled, icon: _jsx(IconSketching, { size: "var(--icon-size-large)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), onClick: () => onToolChange("sketch") })] }), _jsx("div", { className: "canvas-toolbar__divider", "aria-hidden": "true" }), _jsx("div", { className: "canvas-toolbar__tools", role: "group", "aria-label": "Preview tools", children: _jsx(ToolButton, { active: tool === "select", label: "Select", description: "Select (I): use the app normally", icon: _jsx(IconPlayerPlay, { size: "var(--icon-size-large)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), onClick: () => onToolChange("select") }) })] }));
|
|
24
|
+
}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
|
-
export
|
|
2
|
+
export interface CanvasWorkspaceProps {
|
|
3
|
+
readonly primaryUrl: string | null;
|
|
4
|
+
}
|
|
5
|
+
export declare function CanvasWorkspace({ primaryUrl }: CanvasWorkspaceProps): ReactElement | null;
|