nudge-ui 0.2.0 → 0.2.1
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 +872 -12
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
- package/dist/hosts/astro/clientTransport.js +60 -11
- package/dist/hosts/next/sidecar.d.ts +4 -0
- package/dist/hosts/next/sidecar.js +49 -6
- package/dist/hosts/next/wrapper.js +30 -2
- package/dist/hosts/projectBridge.d.ts +20 -0
- package/dist/hosts/projectBridge.js +97 -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 +1 -1
- package/dist/hosts/static/server.js +44 -3
- package/dist/hosts/vite/vite.d.ts +5 -1
- package/dist/hosts/vite/vite.js +78 -14
- package/dist/inspector/agent/McpConnectionDialog.d.ts +2 -2
- package/dist/inspector/agent/McpConnectionDialog.js +11 -35
- package/dist/inspector/agent/client.d.ts +10 -3
- package/dist/inspector/agent/client.js +80 -9
- package/dist/inspector/agent/connectionStatus.js +3 -3
- package/dist/inspector/agent/httpTransport.d.ts +2 -0
- package/dist/inspector/agent/httpTransport.js +37 -17
- package/dist/inspector/agent/protocol.d.ts +8 -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 +246 -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 +392 -73
- 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 +69 -5
- package/dist/inspector/canvas/frameProtocol.js +56 -2
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -0
- package/dist/inspector/canvas/keyboardShortcuts.js +14 -0
- package/dist/inspector/canvas/projection.d.ts +7 -0
- package/dist/inspector/canvas/projection.js +118 -25
- package/dist/inspector/canvas/rendererBootstrap.js +29 -29
- package/dist/inspector/canvas/rendererElementSelector.js +341 -30
- 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/clientManifest.d.ts +5 -0
- package/dist/inspector/clientManifest.js +11 -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 +74 -24
- 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/selectedGeometry.js +21 -2
- package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
- package/dist/inspector/overlay/spacingGestures.js +353 -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 +4 -0
- package/dist/inspector/runtime/runtimeConfig.js +4 -0
- package/dist/inspector/selection/selectionStore.js +2 -0
- package/dist/inspector/settings/SettingsDialog.js +10 -0
- package/dist/inspector/shell/AppShell.js +5 -1
- package/dist/inspector/shell/ChangesLog.js +8 -4
- package/dist/inspector/shell/CopyPromptButton.js +95 -19
- package/dist/inspector/shell/EmptyState.js +1 -1
- package/dist/inspector/shell/InspectorShell.d.ts +0 -1
- package/dist/inspector/shell/InspectorShell.js +37 -38
- 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 +409 -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 +23 -30
- 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 +91 -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 +111 -7
- 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/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/styles.js +13 -11
- package/dist/internal/agent-protocol.d.ts +65 -1
- package/dist/internal/agent-protocol.js +183 -3
- package/dist/nudge-ui.mjs +261 -57
- package/dist/transport/editor.d.ts +28 -0
- package/dist/transport/editor.js +132 -0
- package/dist/transport/index.d.ts +1 -0
- package/dist/transport/index.js +1 -0
- package/dist/transport/routes.d.ts +7 -0
- package/dist/transport/routes.js +7 -0
- package/package.json +3 -1
- 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
6
|
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:
|
|
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,16 @@ 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";
|
|
21
28
|
function projectRect(iframe, rect, zoom) {
|
|
22
29
|
const frameRect = iframe.getBoundingClientRect();
|
|
23
30
|
return {
|
|
@@ -27,14 +34,6 @@ function projectRect(iframe, rect, zoom) {
|
|
|
27
34
|
height: rect.height * zoom,
|
|
28
35
|
};
|
|
29
36
|
}
|
|
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
37
|
function overlayStyle(rect) {
|
|
39
38
|
return {
|
|
40
39
|
position: "fixed",
|
|
@@ -45,6 +44,17 @@ function overlayStyle(rect) {
|
|
|
45
44
|
pointerEvents: "none",
|
|
46
45
|
};
|
|
47
46
|
}
|
|
47
|
+
function spacingGuideStyle(rect) {
|
|
48
|
+
const isHorizontal = rect.width >= rect.height;
|
|
49
|
+
const width = isHorizontal ? Math.min(rect.width, SPACING_GUIDE_HANDLE_LENGTH) : rect.width;
|
|
50
|
+
const height = isHorizontal ? rect.height : Math.min(rect.height, SPACING_GUIDE_HANDLE_LENGTH);
|
|
51
|
+
return overlayStyle({
|
|
52
|
+
left: rect.left + (rect.width - width) / 2,
|
|
53
|
+
top: rect.top + (rect.height - height) / 2,
|
|
54
|
+
width,
|
|
55
|
+
height,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
48
58
|
function findFrameElement(iframe, elementId, cid, src) {
|
|
49
59
|
const doc = iframe.contentDocument;
|
|
50
60
|
if (!doc)
|
|
@@ -55,7 +65,9 @@ function findFrameElement(iframe, elementId, cid, src) {
|
|
|
55
65
|
if (candidates.length !== 1)
|
|
56
66
|
return null;
|
|
57
67
|
const candidate = candidates[0] ?? null;
|
|
58
|
-
if (!candidate
|
|
68
|
+
if (!candidate
|
|
69
|
+
|| candidate.getAttribute("data-cid") !== cid
|
|
70
|
+
|| (candidate.getAttribute("data-src") ?? "") !== src)
|
|
59
71
|
return null;
|
|
60
72
|
const frameWindow = doc.defaultView;
|
|
61
73
|
return frameWindow && candidate instanceof frameWindow.HTMLElement ? candidate : null;
|
|
@@ -79,17 +91,77 @@ function projectGuideToCanvas(guide, zoom) {
|
|
|
79
91
|
target: project(guide.target, true, true),
|
|
80
92
|
};
|
|
81
93
|
}
|
|
94
|
+
function spacingDescriptorMatches(affordance, descriptor) {
|
|
95
|
+
return Boolean(affordance
|
|
96
|
+
&& descriptor
|
|
97
|
+
&& affordance.kind === descriptor.kind
|
|
98
|
+
&& affordance.property === descriptor.property
|
|
99
|
+
&& affordance.side === descriptor.side);
|
|
100
|
+
}
|
|
101
|
+
function clearSpacingDrag(ref) {
|
|
102
|
+
const drag = ref.current;
|
|
103
|
+
ref.current = null;
|
|
104
|
+
if (drag)
|
|
105
|
+
restoreSpacingPreview(drag);
|
|
106
|
+
}
|
|
107
|
+
function applySpacingPreview(drag, point) {
|
|
108
|
+
const value = spacingValueForDrag(drag.affordance, drag.start, point);
|
|
109
|
+
drag.currentValue = value;
|
|
110
|
+
beginLayoutPreview(drag.element, drag.affordance.property);
|
|
111
|
+
drag.element.style.setProperty(drag.affordance.property, spacingValueCss(value));
|
|
112
|
+
}
|
|
113
|
+
function restoreSpacingPreview(drag) {
|
|
114
|
+
if (drag.originalInlineValue) {
|
|
115
|
+
drag.element.style.setProperty(drag.affordance.property, drag.originalInlineValue, drag.originalInlinePriority);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
drag.element.style.removeProperty(drag.affordance.property);
|
|
119
|
+
}
|
|
120
|
+
endLayoutPreview(drag.element, drag.affordance.property);
|
|
121
|
+
}
|
|
122
|
+
function commitSpacingDrag(drag) {
|
|
123
|
+
// Restore the authored declaration before committing the managed rule. The
|
|
124
|
+
// captured start value is the baseline because the pointer remains hovered
|
|
125
|
+
// and CSS inspection can still report the transient preview value.
|
|
126
|
+
restoreSpacingPreview(drag);
|
|
127
|
+
if (drag.currentValue === drag.startValue)
|
|
128
|
+
return;
|
|
129
|
+
setStyles(drag.element, [{
|
|
130
|
+
property: drag.affordance.property,
|
|
131
|
+
value: spacingValueCss(drag.currentValue),
|
|
132
|
+
oldRawValue: spacingValueCss(drag.startValue),
|
|
133
|
+
}]);
|
|
134
|
+
}
|
|
135
|
+
function sendMeasureModifier(iframe, cardId, altKey) {
|
|
136
|
+
iframe.contentWindow?.postMessage({
|
|
137
|
+
type: "measure-modifier",
|
|
138
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
139
|
+
projectId: PROJECT_ID,
|
|
140
|
+
workspaceId: WORKSPACE_ID,
|
|
141
|
+
cardId,
|
|
142
|
+
altKey,
|
|
143
|
+
}, window.location.origin);
|
|
144
|
+
}
|
|
82
145
|
export function CanvasElementOverlay() {
|
|
83
146
|
const [hover, setHover] = useState(null);
|
|
84
147
|
const [measureState, setMeasureState] = useState(null);
|
|
85
148
|
const [, refreshSelectedGeometry] = useReducer((revision) => revision + 1, 0);
|
|
86
149
|
const selected = useSelectedElement();
|
|
87
150
|
const selectedElements = useSelectedElements();
|
|
151
|
+
const inlineTextSession = useInlineTextSession();
|
|
88
152
|
const camera = useBoardCamera();
|
|
89
|
-
|
|
153
|
+
const presentation = useCanvasPresentation();
|
|
154
|
+
const presentationTransitioning = useCanvasPresentationTransitioning();
|
|
155
|
+
const projectionZoom = presentation === "focus" ? 1 : camera.zoom;
|
|
156
|
+
const cards = useCanvasCards(); // Projected geometry must follow card drag and resize updates.
|
|
157
|
+
const cardGeometryKey = cards.map((card) => `${card.id}:${card.x}:${card.y}:${card.width}:${card.height}`).join("|");
|
|
158
|
+
const projectionGeometryKey = `${presentation}:${camera.x}:${camera.y}:${projectionZoom}:${cardGeometryKey}`;
|
|
159
|
+
const previousProjectionGeometryKey = useRef(projectionGeometryKey);
|
|
90
160
|
const dragRef = useRef(null);
|
|
161
|
+
const spacingDragRef = useRef(null);
|
|
162
|
+
const measureAltKeyRef = useRef(false);
|
|
91
163
|
const dropGuide = useDropGuide("canvas");
|
|
92
|
-
const projectedDropGuide = projectGuideToCanvas(dropGuide,
|
|
164
|
+
const projectedDropGuide = projectGuideToCanvas(dropGuide, projectionZoom);
|
|
93
165
|
const selectedFrame = selected?.domElement.ownerDocument.defaultView?.frameElement;
|
|
94
166
|
const selectedInCanvas = selectedFrame instanceof HTMLIFrameElement
|
|
95
167
|
&& selectedFrame.hasAttribute("data-nudge-ui-canvas-renderer");
|
|
@@ -105,16 +177,25 @@ export function CanvasElementOverlay() {
|
|
|
105
177
|
? toRect(selectedFrame.getBoundingClientRect())
|
|
106
178
|
: null;
|
|
107
179
|
const selectedRect = selectedInCanvas && selectedLocalRect
|
|
108
|
-
? projectRect(selectedFrame, selectedLocalRect,
|
|
180
|
+
? projectRect(selectedFrame, selectedLocalRect, projectionZoom)
|
|
109
181
|
: null;
|
|
110
182
|
const projectedSelectedGeometry = selectedInCanvas
|
|
111
183
|
? selectedRects.flatMap((rect, index) => {
|
|
112
184
|
const element = selectedElements[index];
|
|
113
185
|
return rect && element
|
|
114
|
-
? [{ element, rect: projectRect(selectedFrame, rect,
|
|
186
|
+
? [{ element, rect: projectRect(selectedFrame, rect, projectionZoom) }]
|
|
115
187
|
: [];
|
|
116
188
|
})
|
|
117
189
|
: [];
|
|
190
|
+
// Camera transforms commit on the board before iframe viewport geometry is
|
|
191
|
+
// readable. Re-render once after that commit so overlays use the transformed
|
|
192
|
+
// frame rectangle rather than the previous camera position.
|
|
193
|
+
useLayoutEffect(() => {
|
|
194
|
+
const geometryChanged = previousProjectionGeometryKey.current !== projectionGeometryKey;
|
|
195
|
+
previousProjectionGeometryKey.current = projectionGeometryKey;
|
|
196
|
+
if (selectedInCanvas && geometryChanged)
|
|
197
|
+
refreshSelectedGeometry();
|
|
198
|
+
}, [projectionGeometryKey, selectedInCanvas]);
|
|
118
199
|
const selectedBorders = selectedInCanvas && selected
|
|
119
200
|
? readBorderWidths(selected.domElement)
|
|
120
201
|
: null;
|
|
@@ -124,6 +205,32 @@ export function CanvasElementOverlay() {
|
|
|
124
205
|
const stops = selectedElements.map((candidate) => observeSelectedGeometry(candidate.domElement, refreshSelectedGeometry));
|
|
125
206
|
return () => stops.forEach((stop) => stop());
|
|
126
207
|
}, [selected, selectedElements, selectedInCanvas]);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
const broadcastMeasureModifier = (altKey) => {
|
|
210
|
+
measureAltKeyRef.current = altKey;
|
|
211
|
+
for (const [cardId, iframe] of getRegisteredFrames()) {
|
|
212
|
+
sendMeasureModifier(iframe, cardId, altKey);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const onKeyDown = (event) => {
|
|
216
|
+
if (event.key !== "Alt" || measureAltKeyRef.current)
|
|
217
|
+
return;
|
|
218
|
+
broadcastMeasureModifier(true);
|
|
219
|
+
};
|
|
220
|
+
const onKeyUp = (event) => {
|
|
221
|
+
if (event.key === "Alt")
|
|
222
|
+
broadcastMeasureModifier(false);
|
|
223
|
+
};
|
|
224
|
+
const onBlur = () => broadcastMeasureModifier(false);
|
|
225
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
226
|
+
window.addEventListener("keyup", onKeyUp, true);
|
|
227
|
+
window.addEventListener("blur", onBlur);
|
|
228
|
+
return () => {
|
|
229
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
230
|
+
window.removeEventListener("keyup", onKeyUp, true);
|
|
231
|
+
window.removeEventListener("blur", onBlur);
|
|
232
|
+
};
|
|
233
|
+
}, []);
|
|
127
234
|
// The selected element's identity is computed once per selection change
|
|
128
235
|
// (never per hover), so the measurement self-rulers exclusion can compare
|
|
129
236
|
// identities without a per-hover scan of the frame document.
|
|
@@ -135,24 +242,12 @@ export function CanvasElementOverlay() {
|
|
|
135
242
|
return elementId ? { elementId } : null;
|
|
136
243
|
}, [selected?.domElement]);
|
|
137
244
|
useEffect(() => {
|
|
138
|
-
|
|
139
|
-
if (
|
|
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,
|
|
151
|
-
};
|
|
152
|
-
if (!isRendererMessageFor(event.data, frameIdentity))
|
|
245
|
+
const unsubscribe = subscribeCanvasRendererMessages(({ cardId: sourceCardId, iframe: sourceIframe, identity: frameIdentity, message: data, }) => {
|
|
246
|
+
if (data.type === "renderer-hello" || data.type === "frame-ready") {
|
|
247
|
+
sendMeasureModifier(sourceIframe, sourceCardId, measureAltKeyRef.current);
|
|
248
|
+
}
|
|
249
|
+
if (data.type === "renderer-hello")
|
|
153
250
|
return;
|
|
154
|
-
// SAFETY: isRendererMessageFor validated the frame identity and message shape above.
|
|
155
|
-
const data = event.data;
|
|
156
251
|
if (data.type === "element-hover") {
|
|
157
252
|
const msg = data;
|
|
158
253
|
if (!msg.cid)
|
|
@@ -161,13 +256,30 @@ export function CanvasElementOverlay() {
|
|
|
161
256
|
setHover((current) => current?.cardId === sourceCardId ? null : current);
|
|
162
257
|
return;
|
|
163
258
|
}
|
|
259
|
+
const spacingDescriptor = msg.spacing ?? null;
|
|
260
|
+
const hoverPoint = msg.point ?? null;
|
|
261
|
+
const hoverElement = spacingDescriptor
|
|
262
|
+
? findFrameElement(sourceIframe, msg.elementId, msg.cid, msg.src)
|
|
263
|
+
: null;
|
|
264
|
+
const hoverAffordance = spacingDescriptor && hoverElement
|
|
265
|
+
? getSpacingAffordanceForDescriptor(hoverElement, spacingDescriptor)
|
|
266
|
+
: spacingDescriptor && hoverPoint && sourceIframe.contentDocument
|
|
267
|
+
? getSpacingAffordanceAtPoint(sourceIframe.contentDocument, hoverPoint.x, hoverPoint.y)
|
|
268
|
+
: null;
|
|
269
|
+
const matchingAffordance = spacingDescriptorMatches(hoverAffordance, spacingDescriptor)
|
|
270
|
+
? hoverAffordance
|
|
271
|
+
: null;
|
|
272
|
+
const spacingGuides = matchingAffordance?.affectedGuides ?? [];
|
|
273
|
+
const spacingAreas = matchingAffordance?.affectedAreas ?? [];
|
|
164
274
|
setHover({
|
|
165
275
|
iframe: sourceIframe,
|
|
166
276
|
identity: { elementId: msg.elementId },
|
|
167
277
|
rect: msg.rect,
|
|
168
|
-
margins: msg.margins ?? { top: 0, right: 0, bottom: 0, left: 0 },
|
|
169
278
|
borders: msg.borders ?? { top: 0, right: 0, bottom: 0, left: 0 },
|
|
170
279
|
cardId: sourceCardId,
|
|
280
|
+
spacing: spacingGuides.length > 0 ? spacingDescriptor : null,
|
|
281
|
+
spacingGuides,
|
|
282
|
+
spacingAreas,
|
|
171
283
|
});
|
|
172
284
|
}
|
|
173
285
|
else if (data.type === "element-measure-state") {
|
|
@@ -179,14 +291,38 @@ export function CanvasElementOverlay() {
|
|
|
179
291
|
});
|
|
180
292
|
}
|
|
181
293
|
else if (data.type === "element-click") {
|
|
182
|
-
if (!isElementClickMessage(
|
|
294
|
+
if (!isElementClickMessage(data, frameIdentity))
|
|
183
295
|
return;
|
|
184
296
|
const msg = data;
|
|
185
297
|
if (!msg.cid)
|
|
186
298
|
return;
|
|
187
299
|
handleElementClick(msg, sourceIframe, sourceCardId);
|
|
188
300
|
}
|
|
301
|
+
else if (data.type === "inline-text-intent") {
|
|
302
|
+
if (!isInlineTextIntentMessage(data, frameIdentity))
|
|
303
|
+
return;
|
|
304
|
+
if (getSelectedElements().length > 1)
|
|
305
|
+
return;
|
|
306
|
+
const emptyProjection = data.emptyProjectionId
|
|
307
|
+
? Array.from(sourceIframe.contentDocument?.querySelectorAll(`[${EMPTY_TEXT_PROJECTION_ATTR}]`) ?? [])
|
|
308
|
+
.find((candidate) => candidate.getAttribute(EMPTY_TEXT_PROJECTION_ATTR) === data.emptyProjectionId)
|
|
309
|
+
: null;
|
|
310
|
+
const element = emptyProjection ?? findFrameElement(sourceIframe, data.elementId, data.cid, data.src);
|
|
311
|
+
if (!element)
|
|
312
|
+
return;
|
|
313
|
+
handleInlineTextEditIntent(data.intent === "double-click"
|
|
314
|
+
? { kind: "double-click", target: element, point: data.point }
|
|
315
|
+
: {
|
|
316
|
+
kind: "pointer-down",
|
|
317
|
+
target: element,
|
|
318
|
+
point: data.point,
|
|
319
|
+
clickCount: data.clickCount ?? 0,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
189
322
|
else if (data.type === "element-deselect") {
|
|
323
|
+
clearSpacingDrag(spacingDragRef);
|
|
324
|
+
dragRef.current = null;
|
|
325
|
+
clearDropGuide("canvas");
|
|
190
326
|
setSelectedElement(null);
|
|
191
327
|
}
|
|
192
328
|
else if (data.type === "element-drag-start") {
|
|
@@ -196,6 +332,34 @@ export function CanvasElementOverlay() {
|
|
|
196
332
|
const element = findFrameElement(sourceIframe, msg.elementId, msg.cid, msg.src);
|
|
197
333
|
if (!element)
|
|
198
334
|
return;
|
|
335
|
+
clearSpacingDrag(spacingDragRef);
|
|
336
|
+
dragRef.current = null;
|
|
337
|
+
const startPoint = msg.startPoint ?? msg.point;
|
|
338
|
+
const spacing = msg.spacing
|
|
339
|
+
? getSpacingAffordanceForDescriptor(element, msg.spacing)
|
|
340
|
+
: sourceIframe.contentDocument
|
|
341
|
+
? getSpacingAffordanceAtPoint(sourceIframe.contentDocument, startPoint.x, startPoint.y)
|
|
342
|
+
: null;
|
|
343
|
+
const isSpacingDrag = spacing
|
|
344
|
+
&& spacing.element === element
|
|
345
|
+
&& (!msg.spacing || spacingDescriptorMatches(spacing, msg.spacing));
|
|
346
|
+
if (isSpacingDrag) {
|
|
347
|
+
spacingDragRef.current = {
|
|
348
|
+
iframe: sourceIframe,
|
|
349
|
+
element,
|
|
350
|
+
affordance: spacing,
|
|
351
|
+
start: startPoint,
|
|
352
|
+
startValue: spacing.value,
|
|
353
|
+
currentValue: spacing.value,
|
|
354
|
+
originalInlineValue: element.style.getPropertyValue(spacing.property),
|
|
355
|
+
originalInlinePriority: element.style.getPropertyPriority(spacing.property),
|
|
356
|
+
};
|
|
357
|
+
const selectedElement = resolveSelectionFromElement(element);
|
|
358
|
+
if (selectedElement)
|
|
359
|
+
setSelectedElement(selectedElement);
|
|
360
|
+
applySpacingPreview(spacingDragRef.current, msg.point);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
199
363
|
dragRef.current = { iframe: sourceIframe, element };
|
|
200
364
|
const selectedElement = resolveSelectionFromElement(element);
|
|
201
365
|
if (selectedElement)
|
|
@@ -203,12 +367,35 @@ export function CanvasElementOverlay() {
|
|
|
203
367
|
updateDropGuide(msg.point, sourceIframe);
|
|
204
368
|
}
|
|
205
369
|
else if (data.type === "element-drag-move") {
|
|
370
|
+
const spacing = spacingDragRef.current;
|
|
371
|
+
if (spacing && spacing.iframe === sourceIframe) {
|
|
372
|
+
applySpacingPreview(spacing, data.point);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
206
375
|
updateDropGuide(data.point, sourceIframe);
|
|
207
376
|
}
|
|
208
377
|
else if (data.type === "element-drag-end") {
|
|
209
378
|
const msg = data;
|
|
379
|
+
const spacing = spacingDragRef.current;
|
|
380
|
+
if (spacing && spacing.iframe === sourceIframe) {
|
|
381
|
+
spacingDragRef.current = null;
|
|
382
|
+
if (msg.cancelled) {
|
|
383
|
+
restoreSpacingPreview(spacing);
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
applySpacingPreview(spacing, msg.point);
|
|
387
|
+
commitSpacingDrag(spacing);
|
|
388
|
+
}
|
|
389
|
+
clearDropGuide("canvas");
|
|
390
|
+
if (!msg.cancelled) {
|
|
391
|
+
const selectedElement = resolveSelectionFromElement(spacing.element);
|
|
392
|
+
if (selectedElement)
|
|
393
|
+
setSelectedElement(selectedElement);
|
|
394
|
+
}
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
210
397
|
const current = dragRef.current;
|
|
211
|
-
if (current && current.iframe === sourceIframe && sourceIframe.contentDocument) {
|
|
398
|
+
if (!msg.cancelled && current && current.iframe === sourceIframe && sourceIframe.contentDocument) {
|
|
212
399
|
const drop = getDropLocationAtPoint(sourceIframe.contentDocument, current.element, msg.point.x, msg.point.y);
|
|
213
400
|
if (drop)
|
|
214
401
|
moveElement(current.element, drop);
|
|
@@ -249,6 +436,12 @@ export function CanvasElementOverlay() {
|
|
|
249
436
|
undo();
|
|
250
437
|
}
|
|
251
438
|
}
|
|
439
|
+
else if (data.type === "inspector-toggle-request") {
|
|
440
|
+
toggleInspector();
|
|
441
|
+
}
|
|
442
|
+
else if (data.type === "inspector-open-request") {
|
|
443
|
+
setInspectorOpen(true);
|
|
444
|
+
}
|
|
252
445
|
function updateDropGuide(point, iframe) {
|
|
253
446
|
const current = dragRef.current;
|
|
254
447
|
if (!current || current.iframe !== iframe || !iframe.contentDocument)
|
|
@@ -260,23 +453,22 @@ export function CanvasElementOverlay() {
|
|
|
260
453
|
}
|
|
261
454
|
showDropGuide("canvas", iframe.contentDocument, drop);
|
|
262
455
|
}
|
|
263
|
-
}
|
|
264
|
-
window.addEventListener("message", onMessage);
|
|
456
|
+
});
|
|
265
457
|
return () => {
|
|
266
|
-
|
|
458
|
+
unsubscribe();
|
|
459
|
+
clearSpacingDrag(spacingDragRef);
|
|
460
|
+
dragRef.current = null;
|
|
267
461
|
clearDropGuide("canvas");
|
|
268
462
|
};
|
|
269
463
|
}, []);
|
|
464
|
+
if (inlineTextSession && inlineTextSession.host.ownerDocument === selected?.domElement.ownerDocument)
|
|
465
|
+
return null;
|
|
270
466
|
if (!hover && projectedSelectedGeometry.length === 0 && !projectedDropGuide)
|
|
271
467
|
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
|
-
: [];
|
|
468
|
+
const projectedHoverRect = hover ? projectRect(hover.iframe, hover.rect, projectionZoom) : null;
|
|
469
|
+
const projectedSpacingGuides = hover?.spacingGuides.map((guide) => projectRect(hover.iframe, guide, projectionZoom)) ?? [];
|
|
470
|
+
const projectedSpacingAreas = hover?.spacingAreas.map((area) => projectRect(hover.iframe, area, projectionZoom)) ?? [];
|
|
471
|
+
const hoverSpacing = hover?.spacing ?? null;
|
|
280
472
|
const measureStateForSelectedFrame = measureState?.iframe === selectedFrame ? measureState : null;
|
|
281
473
|
const hoverInSelectedFrame = hover?.iframe === selectedFrame ? hover : null;
|
|
282
474
|
const showGuideOverlay = Boolean(selectedElements.length === 1
|
|
@@ -295,7 +487,9 @@ export function CanvasElementOverlay() {
|
|
|
295
487
|
? projectMeasurementSegments(selectedFrameRect, getMeasurementGeometry(selectedLocalRect, hoverInSelectedFrame.rect, {
|
|
296
488
|
selectedBorders: selectedBorders ?? undefined,
|
|
297
489
|
hoveredBorders: hoverInSelectedFrame.borders,
|
|
298
|
-
}).segments,
|
|
490
|
+
}).segments, projectionZoom)
|
|
299
491
|
: [];
|
|
300
|
-
|
|
492
|
+
if (presentationTransitioning)
|
|
493
|
+
return _jsx("style", { "data-test": "canvas-element-overlay-styles", children: overlayStyles });
|
|
494
|
+
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, _jsx("div", { className: "canvas-element-overlay", "data-test": "canvas-hover-outline", style: overlayStyle(projectedHoverRect), "aria-hidden": "true" }), 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}`))), _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
495
|
}
|
|
@@ -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, 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, icon, onClick, }) {
|
|
20
|
+
return (_jsx(IconButton, { variant: "quiet", size: "large", className: "canvas-toolbar__tool", label: label, title: 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 (_jsx("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 === "move", label: "Move", icon: _jsx(IconPointer2, { size: "var(--icon-size-large)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), onClick: () => onToolChange("move") }), _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") })] }) }));
|
|
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;
|