nudge-ui 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/agentCommand.js +4 -28
- package/dist/client.mjs +870 -212
- package/dist/hosts/next/wrapper.js +1 -7
- package/dist/hosts/projectBridge.js +3 -17
- package/dist/inspector/agent/McpConnectionDialog.js +7 -3
- package/dist/inspector/agent/activity.d.ts +1 -1
- package/dist/inspector/agent/activity.js +3 -2
- package/dist/inspector/agent/client.d.ts +2 -2
- package/dist/inspector/agent/client.js +28 -29
- package/dist/inspector/agent/connectionStatus.d.ts +3 -1
- package/dist/inspector/agent/connectionStatus.js +25 -0
- package/dist/inspector/agent/httpTransport.js +31 -147
- package/dist/inspector/agent/protocol.d.ts +1 -1
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +5 -7
- package/dist/inspector/agent/verification.js +67 -20
- package/dist/inspector/boot.d.ts +3 -0
- package/dist/inspector/boot.js +297 -0
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +2 -1
- package/dist/inspector/canvas/AgentActivityOverlay.js +6 -3
- package/dist/inspector/canvas/CanvasCard.js +23 -12
- package/dist/inspector/canvas/CanvasElementOverlay.js +97 -128
- package/dist/inspector/canvas/CanvasToolbar.js +2 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +119 -162
- package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +3 -2
- package/dist/inspector/canvas/LockedWorkspaceNotice.js +10 -6
- package/dist/inspector/canvas/PageCard.d.ts +10 -0
- package/dist/inspector/canvas/PageCard.js +51 -0
- package/dist/inspector/canvas/SketchToolOptions.d.ts +5 -0
- package/dist/inspector/canvas/SketchToolOptions.js +86 -0
- package/dist/inspector/canvas/StaleChangeIndicator.d.ts +1 -1
- package/dist/inspector/canvas/StaleChangeIndicator.js +1 -1
- package/dist/inspector/canvas/ToolOptionsDrawer.d.ts +21 -0
- package/dist/inspector/canvas/ToolOptionsDrawer.js +77 -0
- package/dist/inspector/canvas/activeCanvasDocument.d.ts +3 -2
- package/dist/inspector/canvas/assets/eraser.svg +28 -0
- package/dist/inspector/canvas/assets/pen.svg +25 -0
- package/dist/inspector/canvas/canvasFrame.d.ts +16 -0
- package/dist/inspector/canvas/canvasFrame.js +10 -0
- package/dist/inspector/canvas/canvasGestures.d.ts +0 -1
- package/dist/inspector/canvas/canvasGestures.js +0 -6
- package/dist/inspector/canvas/canvasStore.d.ts +6 -0
- package/dist/inspector/canvas/canvasStore.js +36 -1
- package/dist/inspector/canvas/frameConnection.d.ts +48 -0
- package/dist/inspector/canvas/frameConnection.js +73 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +18 -1
- package/dist/inspector/canvas/frameProtocol.js +36 -0
- package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
- package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
- package/dist/inspector/canvas/pageSurface.d.ts +11 -0
- package/dist/inspector/canvas/pageSurface.js +34 -0
- package/dist/inspector/canvas/projection.d.ts +19 -9
- package/dist/inspector/canvas/projection.js +58 -31
- package/dist/inspector/canvas/rendererBootstrap.js +4 -1
- package/dist/inspector/canvas/rendererElementSelector.js +9 -1
- package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
- package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
- package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
- package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
- package/dist/inspector/canvas/rendererStylesheet.js +15 -0
- package/dist/inspector/canvas/sessionStore.d.ts +2 -2
- package/dist/inspector/canvas/sessionStore.js +12 -9
- package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
- package/dist/inspector/canvas/staleChangeDetector.js +10 -22
- package/dist/inspector/changes/codecs.d.ts +2 -0
- package/dist/inspector/changes/codecs.js +27 -3
- package/dist/inspector/changes/draftPreview.d.ts +12 -0
- package/dist/inspector/changes/draftPreview.js +133 -0
- package/dist/inspector/changes/editModel.d.ts +18 -0
- package/dist/inspector/changes/editModel.js +16 -3
- package/dist/inspector/changes/managedStyleProjection.js +2 -1
- package/dist/inspector/changes/previewDiagnostics.js +1 -1
- package/dist/inspector/changes/types.d.ts +18 -1
- package/dist/inspector/comments/CommentsOverlay.js +18 -16
- package/dist/inspector/comments/element.js +10 -3
- package/dist/inspector/comments/store.js +3 -2
- package/dist/inspector/comments/verification.js +1 -1
- package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
- package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
- package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
- package/dist/inspector/componentSemantics/changeAction.js +1 -1
- package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
- package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
- package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
- package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
- package/dist/inspector/componentSemantics/textBinding.js +4 -45
- package/dist/inspector/componentSemantics/types.d.ts +11 -0
- package/dist/inspector/drafts/draftStore.d.ts +109 -0
- package/dist/inspector/drafts/draftStore.js +451 -0
- package/dist/inspector/drafts/model.d.ts +9 -1
- package/dist/inspector/drafts/model.js +5 -1
- package/dist/inspector/drafts/store.d.ts +53 -15
- package/dist/inspector/drafts/store.js +143 -169
- package/dist/inspector/index.d.ts +2 -4
- package/dist/inspector/index.js +2 -264
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
- package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
- package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
- package/dist/inspector/inspection/bridge.d.ts +1 -1
- package/dist/inspector/inspection/bridge.js +2 -1
- package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
- package/dist/inspector/inspection/browserCssInspection.js +28 -6
- package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
- package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
- package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
- package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
- package/dist/inspector/iterations/capture.d.ts +2 -1
- package/dist/inspector/iterations/capture.js +2 -1
- package/dist/inspector/iterations/client.d.ts +2 -1
- package/dist/inspector/iterations/coordinator.d.ts +4 -3
- package/dist/inspector/iterations/coordinator.js +5 -3
- package/dist/inspector/iterations/prompt.js +9 -8
- package/dist/inspector/overlay/interactionStyles.js +17 -0
- package/dist/inspector/pageInspector.d.ts +42 -0
- package/dist/inspector/pageInspector.js +342 -0
- package/dist/inspector/projection/draftProjection.d.ts +10 -2
- package/dist/inspector/projection/draftProjection.js +9 -1
- package/dist/inspector/projection/managedStylesheet.js +24 -4
- package/dist/inspector/projection/renderedInstance.d.ts +15 -2
- package/dist/inspector/projection/renderedInstance.js +60 -7
- package/dist/inspector/projection/structuralProjection.d.ts +0 -9
- package/dist/inspector/projection/structuralProjection.js +16 -46
- package/dist/inspector/projection/textProjection.d.ts +8 -2
- package/dist/inspector/projection/textProjection.js +77 -24
- package/dist/inspector/prompt/clipboardHandoff.d.ts +14 -0
- package/dist/inspector/prompt/clipboardHandoff.js +47 -16
- package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
- package/dist/inspector/prompt/generatePrompt.js +248 -148
- package/dist/inspector/prompt/promptDocument.d.ts +3 -0
- package/dist/inspector/prompt/promptDocument.js +8 -0
- package/dist/inspector/prompt/promptSettings.js +7 -5
- package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
- package/dist/inspector/runtime/inspectorStorage.js +42 -0
- package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
- package/dist/inspector/runtime/ownedStylesheets.js +72 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
- package/dist/inspector/runtime/runtimeConfig.js +10 -1
- package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
- package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
- package/dist/inspector/selection/editScope.d.ts +1 -0
- package/dist/inspector/selection/editScope.js +13 -2
- package/dist/inspector/selection/sourceSite.d.ts +12 -0
- package/dist/inspector/selection/sourceSite.js +28 -0
- package/dist/inspector/shell/AgentStatusButton.js +2 -2
- package/dist/inspector/shell/AppShell.js +6 -2
- package/dist/inspector/shell/ChangesLog.js +27 -9
- package/dist/inspector/shell/CopyPromptButton.js +74 -26
- package/dist/inspector/shell/EmptyState.js +3 -3
- package/dist/inspector/shell/InspectorShell.d.ts +0 -3
- package/dist/inspector/shell/InspectorShell.js +21 -13
- package/dist/inspector/shell/PeekOriginalButton.js +3 -3
- package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
- package/dist/inspector/shell/interactionStatePreview.js +112 -0
- package/dist/inspector/shell/originalPreview.d.ts +3 -2
- package/dist/inspector/shell/originalPreview.js +10 -37
- package/dist/inspector/shell/stateValue.js +2 -3
- package/dist/inspector/sketch/SketchEditor.js +5 -4
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
- package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
- package/dist/inspector/sketch/SketchOverlay.js +139 -263
- package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
- package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
- package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
- package/dist/inspector/sketch/SketchThumbnail.js +18 -0
- package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
- package/dist/inspector/sketch/SketchWorkspace.js +69 -34
- package/dist/inspector/sketch/capture.js +19 -3
- package/dist/inspector/sketch/copySketch.d.ts +10 -0
- package/dist/inspector/sketch/copySketch.js +52 -0
- package/dist/inspector/sketch/drawingTools.d.ts +17 -0
- package/dist/inspector/sketch/drawingTools.js +47 -0
- package/dist/inspector/sketch/frameHeading.d.ts +9 -0
- package/dist/inspector/sketch/frameHeading.js +30 -0
- package/dist/inspector/sketch/freehand.js +3 -3
- package/dist/inspector/sketch/geometry.d.ts +6 -0
- package/dist/inspector/sketch/geometry.js +19 -0
- package/dist/inspector/sketch/history.d.ts +30 -0
- package/dist/inspector/sketch/history.js +58 -0
- package/dist/inspector/sketch/interaction.d.ts +1 -3
- package/dist/inspector/sketch/interaction.js +6 -9
- package/dist/inspector/sketch/model.d.ts +35 -1
- package/dist/inspector/sketch/model.js +116 -61
- package/dist/inspector/sketch/persistence.d.ts +14 -0
- package/dist/inspector/sketch/persistence.js +63 -7
- package/dist/inspector/sketch/raster.d.ts +6 -2
- package/dist/inspector/sketch/raster.js +43 -17
- package/dist/inspector/sketch/store.d.ts +1 -1
- package/dist/inspector/sketch/store.js +1 -1
- package/dist/inspector/sketch/videoFrames.d.ts +6 -0
- package/dist/inspector/sketch/videoFrames.js +81 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +3 -2
- package/dist/inspector/styleEditors/BorderEditor.js +30 -130
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
- package/dist/inspector/styleEditors/LayoutSection.js +2 -2
- package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
- package/dist/inspector/styleEditors/borderModel.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.js +2 -3
- package/dist/inspector/tokens/TokenField.js +40 -7
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/editActions.d.ts +3 -3
- package/dist/inspector/tokens/editActions.js +15 -3
- package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
- package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
- package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
- package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
- package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
- package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
- package/dist/inspector/tokens/resolution.d.ts +6 -3
- package/dist/inspector/tokens/resolution.js +91 -112
- package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
- package/dist/inspector/tokens/tokenEdits.js +1 -1
- package/dist/inspector/ui/InlineStyleWarning.js +2 -2
- package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
- package/dist/inspector/ui/InspectorStyle.js +26 -0
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +3 -3
- package/dist/inspector/ui/Toast.d.ts +14 -0
- package/dist/inspector/ui/Toast.js +30 -0
- package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
- package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
- package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
- package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
- package/dist/inspector/ui/customCursors.d.ts +5 -0
- package/dist/inspector/ui/customCursors.js +32 -6
- package/dist/inspector/ui/styles.js +12 -10
- package/dist/inspector/workspace/commands.js +1 -2
- package/dist/inspector/workspace/controller.js +1 -2
- package/dist/inspector/workspace/handoff.js +3 -2
- package/dist/inspector/workspace/sentItems.d.ts +16 -0
- package/dist/inspector/workspace/sentItems.js +33 -0
- package/dist/inspector/workspace/timeline.d.ts +24 -10
- package/dist/inspector/workspace/timeline.js +118 -96
- package/dist/internal/agent-protocol.d.ts +50 -13
- package/dist/internal/agent-protocol.js +145 -19
- package/dist/nudge-ui.mjs +81 -65
- package/dist/project/mcpPackage.d.ts +8 -0
- package/dist/project/mcpPackage.js +60 -0
- package/package.json +13 -3
- package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
- package/dist/inspector/canvas/measurementProjection.js +0 -16
- package/dist/inspector/changes/changesLog.d.ts +0 -47
- package/dist/inspector/changes/changesLog.js +0 -247
- package/dist/inspector/changes/draftChanges.d.ts +0 -56
- package/dist/inspector/changes/draftChanges.js +0 -300
- package/dist/inspector/conformance/projection.d.ts +0 -2
- package/dist/inspector/conformance/projection.js +0 -3
- package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
- package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
- package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
- package/dist/inspector/overlay/InspectorOverlay.js +0 -332
- package/dist/inspector/overlay/elementSelector.d.ts +0 -1
- package/dist/inspector/overlay/elementSelector.js +0 -111
- package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
- package/dist/inspector/sketch/SketchChanges.js +0 -72
- package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
- package/dist/inspector/sketch/SketchEntryButton.js +0 -11
- package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
- package/dist/inspector/sketch/SketchLayersPanel.js +0 -71
|
@@ -1,27 +1,34 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { Dialog } from "@base-ui/react/dialog";
|
|
4
|
-
import { IconCheck, IconX, } from "@tabler/icons-react";
|
|
5
|
-
import { Button } from "../ui/Button.js";
|
|
6
|
-
import { IconButton } from "../ui/IconButton.js";
|
|
7
4
|
import { getSketchViewport } from "./capture.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { SKETCH_LIMITS, SKETCH_STROKE_OUTLINE, SKETCH_RECTANGLE_STROKE_WIDTH, SKETCH_STROKE_WIDTH, } from "./model.js";
|
|
6
|
+
import { findErasedSketchStrokeIds } from "./geometry.js";
|
|
7
|
+
import { EMPTY_SKETCH_HISTORY, addSketchStroke, eraseSketchStrokes, redoSketchEdit, undoSketchEdit, } from "./history.js";
|
|
8
|
+
import { resetSketchDrawingTool, useSketchDrawingTools } from "./drawingTools.js";
|
|
10
9
|
import { SketchPromptPanel } from "./SketchPromptPanel.js";
|
|
11
10
|
import { SketchSvgLayer } from "./freehand.js";
|
|
12
11
|
import { useInspectorOpen } from "../shell/openStore.js";
|
|
13
|
-
import {
|
|
12
|
+
import { isEditableEvent } from "../shell/shortcuts.js";
|
|
13
|
+
import { ERASER_CURSOR, SKETCH_CURSOR } from "../ui/customCursors.js";
|
|
14
14
|
import { useTemporaryAppInteraction } from "../canvas/viewStore.js";
|
|
15
|
-
import { Tooltip } from "../ui/Tooltip.js";
|
|
16
15
|
import { portalContainer } from "../ui/portalContainer.js";
|
|
16
|
+
/** Eraser reach around the pointer in screen pixels, matching the cursor ring. */
|
|
17
|
+
const ERASER_RADIUS_PX = 8;
|
|
18
|
+
function sketchCursor(tool) {
|
|
19
|
+
if (tool === "eraser")
|
|
20
|
+
return ERASER_CURSOR;
|
|
21
|
+
if (tool === "rectangle")
|
|
22
|
+
return "crosshair";
|
|
23
|
+
return SKETCH_CURSOR;
|
|
24
|
+
}
|
|
17
25
|
function clamp(value, maximum) {
|
|
18
26
|
return Math.max(0, Math.min(maximum, value));
|
|
19
27
|
}
|
|
20
|
-
function pointFromEvent(event,
|
|
21
|
-
const bounds = canvas.getBoundingClientRect();
|
|
28
|
+
function pointFromEvent(event, viewport) {
|
|
22
29
|
return {
|
|
23
|
-
x: clamp(((event.clientX -
|
|
24
|
-
y: clamp(((event.clientY -
|
|
30
|
+
x: clamp(((event.clientX - viewport.offsetX) / Math.max(1, viewport.displayWidth)) * viewport.width, viewport.width) + viewport.scrollX,
|
|
31
|
+
y: clamp(((event.clientY - viewport.offsetY) / Math.max(1, viewport.displayHeight)) * viewport.height, viewport.height) + viewport.scrollY,
|
|
25
32
|
};
|
|
26
33
|
}
|
|
27
34
|
function distance(left, right) {
|
|
@@ -36,15 +43,6 @@ function visibleStroke(stroke, viewport) {
|
|
|
36
43
|
})),
|
|
37
44
|
};
|
|
38
45
|
}
|
|
39
|
-
function visibleAnnotation(annotation, viewport) {
|
|
40
|
-
return {
|
|
41
|
-
...annotation,
|
|
42
|
-
point: {
|
|
43
|
-
x: annotation.point.x - viewport.scrollX,
|
|
44
|
-
y: annotation.point.y - viewport.scrollY,
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
46
|
function rectanglePoints(start, end) {
|
|
49
47
|
const left = Math.min(start.x, end.x);
|
|
50
48
|
const right = Math.max(start.x, end.x);
|
|
@@ -58,58 +56,52 @@ function rectanglePoints(start, end) {
|
|
|
58
56
|
{ x: left, y: top },
|
|
59
57
|
];
|
|
60
58
|
}
|
|
61
|
-
function
|
|
62
|
-
const width = 280;
|
|
63
|
-
const height = 52;
|
|
64
|
-
return {
|
|
65
|
-
left: clamp(annotation.point.x - viewport.scrollX + SKETCH_ANNOTATION_RADIUS + 8, Math.max(8, viewport.width - width - 8)),
|
|
66
|
-
top: clamp(annotation.point.y - viewport.scrollY + SKETCH_ANNOTATION_RADIUS + 8, Math.max(8, viewport.height - height - 8)),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
export function SketchOverlay({ open, hostElement, initialTool = "pen", onCancel, onDone }) {
|
|
59
|
+
export function SketchOverlay({ open, hostElement, selectTarget, onCancel, onDone }) {
|
|
70
60
|
const temporaryAppInteraction = useTemporaryAppInteraction();
|
|
71
61
|
const inspectorOpen = useInspectorOpen();
|
|
72
62
|
const [viewport, setViewport] = useState(() => getSketchViewport(hostElement));
|
|
73
|
-
const
|
|
63
|
+
const drawing = useSketchDrawingTools();
|
|
64
|
+
const tool = drawing.tool;
|
|
74
65
|
const [description, setDescription] = useState("");
|
|
75
|
-
const [
|
|
76
|
-
const [redoStrokes, setRedoStrokes] = useState([]);
|
|
77
|
-
const [annotations, setAnnotations] = useState([]);
|
|
78
|
-
const [activeAnnotationId, setActiveAnnotationId] = useState(null);
|
|
79
|
-
const [activeAnnotationOriginal, setActiveAnnotationOriginal] = useState(null);
|
|
80
|
-
const [annotationDraft, setAnnotationDraft] = useState("");
|
|
66
|
+
const [history, setHistory] = useState(EMPTY_SKETCH_HISTORY);
|
|
81
67
|
const [draftPoints, setDraftPoints] = useState([]);
|
|
82
68
|
const [submitting, setSubmitting] = useState(false);
|
|
83
69
|
const [error, setError] = useState(null);
|
|
84
70
|
const svgRef = useRef(null);
|
|
85
|
-
const
|
|
71
|
+
const targetRef = useRef(hostElement);
|
|
86
72
|
const pointerId = useRef(null);
|
|
87
|
-
const
|
|
73
|
+
const draftPointsRef = useRef([]);
|
|
88
74
|
const draftPointLimitReached = useRef(false);
|
|
89
|
-
const
|
|
75
|
+
const eraserPoint = useRef(null);
|
|
76
|
+
// Identifies the current eraser drag so it undoes as a single edit.
|
|
77
|
+
const eraseGesture = useRef(0);
|
|
78
|
+
const strokes = history.strokes;
|
|
79
|
+
function updateDraftPoints(next) {
|
|
80
|
+
draftPointsRef.current = next;
|
|
81
|
+
setDraftPoints(next);
|
|
82
|
+
}
|
|
90
83
|
useEffect(() => {
|
|
91
84
|
if (!open)
|
|
92
85
|
return;
|
|
86
|
+
targetRef.current = hostElement;
|
|
93
87
|
setViewport(getSketchViewport(hostElement));
|
|
94
|
-
|
|
95
|
-
// still opens directly in annotation mode without the old live toolbar.
|
|
96
|
-
setTool(initialTool);
|
|
88
|
+
resetSketchDrawingTool();
|
|
97
89
|
setDescription("");
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
setAnnotationDraft("");
|
|
104
|
-
setDraftPoints([]);
|
|
105
|
-
moveState.current = null;
|
|
90
|
+
setHistory(EMPTY_SKETCH_HISTORY);
|
|
91
|
+
updateDraftPoints([]);
|
|
92
|
+
// Closing mid-stroke unmounts the surface before its pointerup arrives.
|
|
93
|
+
pointerId.current = null;
|
|
94
|
+
eraserPoint.current = null;
|
|
106
95
|
setSubmitting(false);
|
|
107
96
|
setError(null);
|
|
108
|
-
|
|
97
|
+
// Choosing the first stroke's frame must preserve the stroke already in progress.
|
|
98
|
+
}, [open]);
|
|
109
99
|
useEffect(() => {
|
|
110
100
|
if (!open)
|
|
111
101
|
return;
|
|
102
|
+
targetRef.current = hostElement;
|
|
112
103
|
const updateViewport = () => setViewport(getSketchViewport(hostElement));
|
|
104
|
+
updateViewport();
|
|
113
105
|
window.addEventListener("resize", updateViewport);
|
|
114
106
|
window.addEventListener("scroll", updateViewport, true);
|
|
115
107
|
const frameWindow = typeof HTMLIFrameElement !== "undefined"
|
|
@@ -125,155 +117,90 @@ export function SketchOverlay({ open, hostElement, initialTool = "pen", onCancel
|
|
|
125
117
|
frameWindow?.removeEventListener("scroll", updateViewport, true);
|
|
126
118
|
};
|
|
127
119
|
}, [hostElement, open]);
|
|
128
|
-
useEffect(() => {
|
|
129
|
-
if (activeAnnotationId !== null)
|
|
130
|
-
annotationInputRef.current?.focus();
|
|
131
|
-
}, [activeAnnotationId]);
|
|
132
120
|
useEffect(() => {
|
|
133
121
|
if (open)
|
|
134
122
|
svgRef.current?.focus();
|
|
135
123
|
}, [open]);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
const next = annotations.map((annotation) => annotation.id === activeAnnotationId
|
|
155
|
-
? { ...annotation, description }
|
|
156
|
-
: annotation);
|
|
157
|
-
setAnnotations(next);
|
|
158
|
-
setActiveAnnotationId(null);
|
|
159
|
-
setActiveAnnotationOriginal(null);
|
|
160
|
-
setAnnotationDraft("");
|
|
161
|
-
setError(null);
|
|
162
|
-
return next;
|
|
163
|
-
}
|
|
164
|
-
function cancelActiveAnnotation() {
|
|
165
|
-
if (activeAnnotationId !== null && activeAnnotationOriginal === "") {
|
|
166
|
-
setAnnotations((current) => current.filter((annotation) => annotation.id !== activeAnnotationId));
|
|
124
|
+
// Listen on the window rather than the dialog: the drawing options live in
|
|
125
|
+
// the canvas toolbar, so focus is often outside the dialog while sketching.
|
|
126
|
+
// Text fields keep their native undo.
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (!open)
|
|
129
|
+
return;
|
|
130
|
+
function onKeyDown(event) {
|
|
131
|
+
if (!(event.metaKey || event.ctrlKey) || event.altKey || isEditableEvent(event))
|
|
132
|
+
return;
|
|
133
|
+
const key = event.key.toLowerCase();
|
|
134
|
+
const isRedo = (key === "z" && event.shiftKey) || (key === "y" && !event.shiftKey);
|
|
135
|
+
const isUndo = key === "z" && !event.shiftKey;
|
|
136
|
+
if (!isRedo && !isUndo)
|
|
137
|
+
return;
|
|
138
|
+
event.preventDefault();
|
|
139
|
+
event.stopPropagation();
|
|
140
|
+
setHistory((current) => isRedo ? redoSketchEdit(current) : undoSketchEdit(current));
|
|
167
141
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
142
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
143
|
+
return () => window.removeEventListener("keydown", onKeyDown, true);
|
|
144
|
+
}, [open]);
|
|
145
|
+
function eraseAlong(start, end, drawingViewport) {
|
|
146
|
+
const radius = ERASER_RADIUS_PX * (drawingViewport.width / Math.max(1, drawingViewport.displayWidth));
|
|
147
|
+
const gesture = eraseGesture.current;
|
|
148
|
+
setHistory((current) => eraseSketchStrokes(current, findErasedSketchStrokeIds(start, end, current.strokes, radius), gesture));
|
|
172
149
|
}
|
|
173
150
|
function onPointerDown(event) {
|
|
174
|
-
if (submitting || pointerId.current !== null
|
|
151
|
+
if (submitting || pointerId.current !== null)
|
|
175
152
|
return;
|
|
176
|
-
|
|
177
|
-
if (
|
|
178
|
-
event.
|
|
179
|
-
|
|
180
|
-
if (!element)
|
|
181
|
-
return;
|
|
182
|
-
moveState.current = { pointerId: event.pointerId, element, lastPoint: point };
|
|
183
|
-
event.currentTarget.setPointerCapture(event.pointerId);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
if (tool === "annotate") {
|
|
187
|
-
event.preventDefault();
|
|
188
|
-
if (activeAnnotationId !== null) {
|
|
189
|
-
if (commitActiveAnnotation())
|
|
190
|
-
setError("Select Annotate again, then click another area.");
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const existing = annotationAt(point);
|
|
194
|
-
if (existing) {
|
|
195
|
-
openAnnotation(existing);
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (annotations.length >= SKETCH_LIMITS.annotations) {
|
|
199
|
-
setError(`A sketch can contain at most ${SKETCH_LIMITS.annotations} annotations.`);
|
|
153
|
+
let drawingViewport = getSketchViewport(targetRef.current);
|
|
154
|
+
if (selectTarget) {
|
|
155
|
+
const target = selectTarget({ x: event.clientX, y: event.clientY }, history.undo.length > 0 || history.redo.length > 0 ? targetRef.current : null);
|
|
156
|
+
if (!target)
|
|
200
157
|
return;
|
|
158
|
+
if (target !== targetRef.current) {
|
|
159
|
+
targetRef.current = target;
|
|
160
|
+
drawingViewport = getSketchViewport(target);
|
|
161
|
+
setViewport(drawingViewport);
|
|
201
162
|
}
|
|
202
|
-
const usedNumbers = new Set(annotations.map((annotation) => annotation.number));
|
|
203
|
-
let nextNumber = 1;
|
|
204
|
-
while (usedNumbers.has(nextNumber))
|
|
205
|
-
nextNumber += 1;
|
|
206
|
-
const annotation = {
|
|
207
|
-
id: `annotation-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
208
|
-
number: nextNumber,
|
|
209
|
-
point,
|
|
210
|
-
description: "",
|
|
211
|
-
};
|
|
212
|
-
setAnnotations((current) => [...current, annotation]);
|
|
213
|
-
setActiveAnnotationId(annotation.id);
|
|
214
|
-
setActiveAnnotationOriginal("");
|
|
215
|
-
setAnnotationDraft("");
|
|
216
|
-
setError(null);
|
|
217
|
-
return;
|
|
218
163
|
}
|
|
164
|
+
if (event.clientX < drawingViewport.offsetX || event.clientX > drawingViewport.offsetX + drawingViewport.displayWidth
|
|
165
|
+
|| event.clientY < drawingViewport.offsetY || event.clientY > drawingViewport.offsetY + drawingViewport.displayHeight)
|
|
166
|
+
return;
|
|
167
|
+
const point = pointFromEvent(event, drawingViewport);
|
|
168
|
+
// Drawing cancels the browser's focus change, so move focus here
|
|
169
|
+
// explicitly; otherwise undo keeps going to the note or a toolbar button.
|
|
219
170
|
event.preventDefault();
|
|
171
|
+
svgRef.current?.focus({ preventScroll: true });
|
|
220
172
|
pointerId.current = event.pointerId;
|
|
221
|
-
draftPointLimitReached.current = false;
|
|
222
173
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (activeMove?.pointerId === event.pointerId) {
|
|
228
|
-
event.preventDefault();
|
|
229
|
-
const point = pointFromEvent(event, event.currentTarget, viewport);
|
|
230
|
-
const delta = {
|
|
231
|
-
x: point.x - activeMove.lastPoint.x,
|
|
232
|
-
y: point.y - activeMove.lastPoint.y,
|
|
233
|
-
};
|
|
234
|
-
if (delta.x !== 0 || delta.y !== 0) {
|
|
235
|
-
const bounds = { width: SKETCH_LIMITS.imageEdge, height: SKETCH_LIMITS.imageEdge };
|
|
236
|
-
if (activeMove.element.kind === "stroke") {
|
|
237
|
-
setStrokes((current) => current.map((stroke) => stroke.id === activeMove.element.id
|
|
238
|
-
? translateSketchStroke(stroke, delta, bounds)
|
|
239
|
-
: stroke));
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
setAnnotations((current) => current.map((annotation) => annotation.id === activeMove.element.id
|
|
243
|
-
? translateSketchAnnotation(annotation, delta, bounds)
|
|
244
|
-
: annotation));
|
|
245
|
-
}
|
|
246
|
-
activeMove.lastPoint = point;
|
|
247
|
-
}
|
|
174
|
+
if (tool === "eraser") {
|
|
175
|
+
eraserPoint.current = point;
|
|
176
|
+
eraseGesture.current += 1;
|
|
177
|
+
eraseAlong(point, point, drawingViewport);
|
|
248
178
|
return;
|
|
249
179
|
}
|
|
180
|
+
draftPointLimitReached.current = false;
|
|
181
|
+
updateDraftPoints([point]);
|
|
182
|
+
}
|
|
183
|
+
function onPointerMove(event) {
|
|
250
184
|
if (pointerId.current !== event.pointerId)
|
|
251
185
|
return;
|
|
252
186
|
event.preventDefault();
|
|
253
|
-
const point = pointFromEvent(event,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return previous;
|
|
258
|
-
if (previous.length >= SKETCH_LIMITS.points) {
|
|
259
|
-
draftPointLimitReached.current = true;
|
|
260
|
-
return previous;
|
|
261
|
-
}
|
|
262
|
-
return [...previous, point];
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
function finishPointer(event) {
|
|
266
|
-
const activeMove = moveState.current;
|
|
267
|
-
if (activeMove?.pointerId === event.pointerId) {
|
|
268
|
-
moveState.current = null;
|
|
269
|
-
try {
|
|
270
|
-
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
271
|
-
}
|
|
272
|
-
catch {
|
|
273
|
-
// The pointer may already have been released by the browser.
|
|
274
|
-
}
|
|
187
|
+
const point = pointFromEvent(event, viewport);
|
|
188
|
+
if (eraserPoint.current) {
|
|
189
|
+
eraseAlong(eraserPoint.current, point, viewport);
|
|
190
|
+
eraserPoint.current = point;
|
|
275
191
|
return;
|
|
276
192
|
}
|
|
193
|
+
const previous = draftPointsRef.current;
|
|
194
|
+
const last = previous.at(-1);
|
|
195
|
+
if (last && distance(last, point) < 1)
|
|
196
|
+
return;
|
|
197
|
+
if (previous.length >= SKETCH_LIMITS.points) {
|
|
198
|
+
draftPointLimitReached.current = true;
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
updateDraftPoints([...previous, point]);
|
|
202
|
+
}
|
|
203
|
+
function finishPointer(event) {
|
|
277
204
|
if (pointerId.current !== event.pointerId)
|
|
278
205
|
return;
|
|
279
206
|
pointerId.current = null;
|
|
@@ -283,92 +210,46 @@ export function SketchOverlay({ open, hostElement, initialTool = "pen", onCancel
|
|
|
283
210
|
catch {
|
|
284
211
|
// The pointer may already have been released by the browser.
|
|
285
212
|
}
|
|
213
|
+
if (eraserPoint.current) {
|
|
214
|
+
eraserPoint.current = null;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const previous = draftPointsRef.current;
|
|
218
|
+
updateDraftPoints([]);
|
|
286
219
|
if (draftPointLimitReached.current) {
|
|
287
220
|
draftPointLimitReached.current = false;
|
|
288
|
-
setDraftPoints([]);
|
|
289
221
|
setError(`A stroke can contain at most ${SKETCH_LIMITS.points.toLocaleString()} points. The incomplete stroke was discarded.`);
|
|
290
222
|
return;
|
|
291
223
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return [];
|
|
302
|
-
}
|
|
303
|
-
if (points.length < 2)
|
|
304
|
-
return [];
|
|
305
|
-
if (tool === "rectangle" && distance(previous[0], previous.at(-1)) < 1)
|
|
306
|
-
return [];
|
|
307
|
-
const stroke = {
|
|
308
|
-
id: `stroke-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
309
|
-
kind: tool === "rectangle" ? "rectangle" : "freehand",
|
|
310
|
-
width: SKETCH_STROKE_WIDTH,
|
|
311
|
-
color: SKETCH_STROKE_COLOR,
|
|
312
|
-
outlineColor: SKETCH_STROKE_OUTLINE,
|
|
313
|
-
points: points.map((point) => ({ ...point })),
|
|
314
|
-
};
|
|
315
|
-
setStrokes((current) => [...current, stroke]);
|
|
316
|
-
setRedoStrokes([]);
|
|
317
|
-
return [];
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
function undo() {
|
|
321
|
-
setStrokes((current) => {
|
|
322
|
-
const last = current.at(-1);
|
|
323
|
-
if (!last)
|
|
324
|
-
return current;
|
|
325
|
-
setRedoStrokes((redo) => [...redo, last]);
|
|
326
|
-
return current.slice(0, -1);
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
function redo() {
|
|
330
|
-
setRedoStrokes((current) => {
|
|
331
|
-
const last = current.at(-1);
|
|
332
|
-
if (!last)
|
|
333
|
-
return current;
|
|
334
|
-
setStrokes((existing) => [...existing, last]);
|
|
335
|
-
return current.slice(0, -1);
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
function onEditorKeyDown(event) {
|
|
339
|
-
const mod = event.metaKey || event.ctrlKey;
|
|
340
|
-
if (mod && event.key.toLowerCase() === "z") {
|
|
341
|
-
event.preventDefault();
|
|
342
|
-
event.stopPropagation();
|
|
343
|
-
if (event.shiftKey)
|
|
344
|
-
redo();
|
|
345
|
-
else
|
|
346
|
-
undo();
|
|
347
|
-
}
|
|
348
|
-
else if (mod && event.key.toLowerCase() === "y") {
|
|
349
|
-
event.preventDefault();
|
|
350
|
-
event.stopPropagation();
|
|
351
|
-
redo();
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
function onAnnotationKeyDown(event) {
|
|
355
|
-
if (event.key === "Enter") {
|
|
356
|
-
event.preventDefault();
|
|
357
|
-
commitActiveAnnotation();
|
|
358
|
-
}
|
|
359
|
-
else if (event.key === "Escape") {
|
|
360
|
-
event.preventDefault();
|
|
361
|
-
cancelActiveAnnotation();
|
|
224
|
+
if (previous.length === 0)
|
|
225
|
+
return;
|
|
226
|
+
const points = tool === "rectangle" && previous.length > 1
|
|
227
|
+
? rectanglePoints(previous[0], previous.at(-1))
|
|
228
|
+
: previous;
|
|
229
|
+
const totalPoints = strokes.reduce((total, stroke) => total + stroke.points.length, 0) + points.length;
|
|
230
|
+
if (strokes.length >= SKETCH_LIMITS.strokes || totalPoints > SKETCH_LIMITS.points) {
|
|
231
|
+
setError("The sketch has reached its stroke limit. Erase a mark before drawing again.");
|
|
232
|
+
return;
|
|
362
233
|
}
|
|
234
|
+
if (points.length < 2)
|
|
235
|
+
return;
|
|
236
|
+
if (tool === "rectangle" && distance(previous[0], previous.at(-1)) < 1)
|
|
237
|
+
return;
|
|
238
|
+
const stroke = {
|
|
239
|
+
id: `stroke-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
240
|
+
kind: tool === "rectangle" ? "rectangle" : "freehand",
|
|
241
|
+
width: tool === "rectangle" ? SKETCH_RECTANGLE_STROKE_WIDTH : SKETCH_STROKE_WIDTH,
|
|
242
|
+
color: drawing.color.value,
|
|
243
|
+
outlineColor: SKETCH_STROKE_OUTLINE,
|
|
244
|
+
points: points.map((point) => ({ ...point })),
|
|
245
|
+
};
|
|
246
|
+
setHistory((current) => addSketchStroke(current, stroke));
|
|
363
247
|
}
|
|
364
248
|
async function done() {
|
|
365
249
|
if (submitting)
|
|
366
250
|
return;
|
|
367
|
-
const committedAnnotations = commitActiveAnnotation();
|
|
368
|
-
if (committedAnnotations === null)
|
|
369
|
-
return;
|
|
370
251
|
const trimmedDescription = description.trim();
|
|
371
|
-
if (strokes.length === 0 &&
|
|
252
|
+
if (strokes.length === 0 && trimmedDescription === "") {
|
|
372
253
|
setError("Add a mark or a short description before saving the sketch.");
|
|
373
254
|
return;
|
|
374
255
|
}
|
|
@@ -381,10 +262,6 @@ export function SketchOverlay({ open, hostElement, initialTool = "pen", onCancel
|
|
|
381
262
|
...stroke,
|
|
382
263
|
points: stroke.points.map((point) => ({ ...point })),
|
|
383
264
|
})),
|
|
384
|
-
annotations: committedAnnotations.map((annotation) => ({
|
|
385
|
-
...annotation,
|
|
386
|
-
point: { ...annotation.point },
|
|
387
|
-
})),
|
|
388
265
|
viewport: getSketchViewport(hostElement),
|
|
389
266
|
});
|
|
390
267
|
}
|
|
@@ -394,12 +271,11 @@ export function SketchOverlay({ open, hostElement, initialTool = "pen", onCancel
|
|
|
394
271
|
}
|
|
395
272
|
}
|
|
396
273
|
const visibleStrokes = strokes.map((stroke) => visibleStroke(stroke, viewport));
|
|
397
|
-
const visibleAnnotations = annotations.map((annotation) => visibleAnnotation(annotation, viewport));
|
|
398
274
|
const draftStroke = draftPoints.length > 0 ? {
|
|
399
275
|
id: "draft",
|
|
400
276
|
kind: tool === "rectangle" ? "rectangle" : "freehand",
|
|
401
|
-
width: SKETCH_STROKE_WIDTH,
|
|
402
|
-
color:
|
|
277
|
+
width: tool === "rectangle" ? SKETCH_RECTANGLE_STROKE_WIDTH : SKETCH_STROKE_WIDTH,
|
|
278
|
+
color: drawing.color.value,
|
|
403
279
|
outlineColor: SKETCH_STROKE_OUTLINE,
|
|
404
280
|
points: (tool === "rectangle" && draftPoints.length > 1
|
|
405
281
|
? rectanglePoints(draftPoints[0], draftPoints.at(-1))
|
|
@@ -411,10 +287,10 @@ export function SketchOverlay({ open, hostElement, initialTool = "pen", onCancel
|
|
|
411
287
|
if (!open)
|
|
412
288
|
return null;
|
|
413
289
|
return (_jsx(Dialog.Root, { open: open, modal: false, disablePointerDismissal: true, onOpenChange: (nextOpen) => { if (!nextOpen && !submitting)
|
|
414
|
-
onCancel(); }, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { style: { visibility: temporaryAppInteraction ? "hidden" : undefined }, className: "sketch__backdrop sketch__live-backdrop", "data-inspector-open": inspectorOpen ? "true" : "false", "data-test": "sketch-live-backdrop" }), _jsxs(Dialog.Popup, { style: { visibility: temporaryAppInteraction ? "hidden" : undefined }, className: "sketch__popup sketch__live-popup", "data-inspector-open": inspectorOpen ? "true" : "false", "data-test": "sketch-live-editor",
|
|
290
|
+
onCancel(); }, children: _jsxs(Dialog.Portal, { container: portalContainer(), children: [_jsx(Dialog.Backdrop, { style: { visibility: temporaryAppInteraction ? "hidden" : undefined }, className: "sketch__backdrop sketch__live-backdrop", "data-inspector-open": inspectorOpen ? "true" : "false", "data-test": "sketch-live-backdrop" }), _jsxs(Dialog.Popup, { style: { visibility: temporaryAppInteraction ? "hidden" : undefined }, className: "sketch__popup sketch__live-popup", "data-inspector-open": inspectorOpen ? "true" : "false", "data-test": "sketch-live-editor", children: [_jsx(Dialog.Title, { className: "sketch__title sketch__sr-only", children: "Sketch viewport" }), _jsx(Dialog.Description, { className: "sketch__description sketch__sr-only", children: "Draw directly over the live interface, then add a short note for the sketch." }), selectTarget ? (_jsx("svg", { className: "sketch__target-surface", "aria-hidden": "true", style: { cursor: sketchCursor(tool) }, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: finishPointer, onPointerCancel: finishPointer })) : null, _jsx("div", { className: "sketch__canvas-frame sketch__live-frame", style: {
|
|
415
291
|
left: viewport.offsetX,
|
|
416
292
|
top: viewport.offsetY,
|
|
417
293
|
width: viewport.displayWidth,
|
|
418
294
|
height: viewport.displayHeight,
|
|
419
|
-
}, children:
|
|
295
|
+
}, children: _jsx("svg", { ref: svgRef, className: `sketch__canvas sketch__live-canvas sketch__canvas--${tool}`, width: viewport.width, height: viewport.height, viewBox: `0 0 ${viewport.width} ${viewport.height}`, preserveAspectRatio: "none", pointerEvents: "all", style: { cursor: sketchCursor(tool) }, tabIndex: 0, "aria-label": "Sketch over the live interface", onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: finishPointer, onPointerCancel: finishPointer, children: _jsx(SketchSvgLayer, { strokes: draftStroke ? [...visibleStrokes, draftStroke] : visibleStrokes }) }) }), _jsx(SketchPromptPanel, { dataTest: "sketch-live-prompt", className: "sketch__live-note-panel", description: description, error: error, saving: submitting, autoFocus: true, onDescriptionChange: setDescription, onDone: done, onCancel: onCancel })] })] }) }));
|
|
420
296
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { IconAlertTriangleFilled } from "@tabler/icons-react";
|
|
3
|
+
import { useRef } from "react";
|
|
2
4
|
import { Button } from "../ui/Button.js";
|
|
3
5
|
import { SKETCH_LIMITS } from "./model.js";
|
|
4
6
|
/** The small note editor shared by a new live sketch and saved sketch layers. */
|
|
5
7
|
export function SketchPromptPanel({ dataTest, className, description, error = null, disabled = false, saving = false, autoFocus = false, onDescriptionChange, onDone, onCancel, }) {
|
|
8
|
+
// Keep the last message while the banner collapses so its text does not vanish mid-animation.
|
|
9
|
+
const lastError = useRef(error);
|
|
10
|
+
if (error)
|
|
11
|
+
lastError.current = error;
|
|
12
|
+
const bannerMessage = error ?? lastError.current;
|
|
6
13
|
function onKeyDown(event) {
|
|
7
14
|
if (event.key === "Escape" && onCancel) {
|
|
8
15
|
event.preventDefault();
|
|
@@ -14,5 +21,5 @@ export function SketchPromptPanel({ dataTest, className, description, error = nu
|
|
|
14
21
|
void onDone();
|
|
15
22
|
}
|
|
16
23
|
}
|
|
17
|
-
return (_jsxs("section", { className: `sketch__prompt-panel${className ? ` ${className}` : ""}`, role: "dialog", "aria-label": "Sketch note", "data-test": dataTest, children: [_jsx("label", { className: "sketch__prompt-label sketch__sr-only", htmlFor: `${dataTest}-input`, children: "Sketch note" }), _jsx("textarea", { id: `${dataTest}-input`, className: "sketch__prompt-input", value: description, rows: 2, maxLength: SKETCH_LIMITS.description, placeholder: "Add a note to your sketch", "aria-label": "Sketch note", "data-test": `${dataTest}-input`, autoFocus: autoFocus, disabled: disabled, onChange: (event) => onDescriptionChange(event.target.value), onKeyDown: onKeyDown }),
|
|
24
|
+
return (_jsxs("section", { className: `sketch__prompt-panel${className ? ` ${className}` : ""}`, role: "dialog", "aria-label": "Sketch note", "data-test": dataTest, children: [_jsx("div", { className: "sketch__prompt-error", "data-open": error ? "true" : "false", "aria-hidden": error ? undefined : true, children: _jsx("div", { className: "sketch__prompt-error-clip", children: _jsxs("div", { className: "sketch__prompt-error-body", role: "status", children: [_jsx(IconAlertTriangleFilled, { className: "sketch__prompt-error-icon", size: 16, "aria-hidden": "true" }), _jsx("span", { children: bannerMessage })] }) }) }), _jsx("label", { className: "sketch__prompt-label sketch__sr-only", htmlFor: `${dataTest}-input`, children: "Sketch note" }), _jsx("textarea", { id: `${dataTest}-input`, className: "sketch__prompt-input", value: description, rows: 2, maxLength: SKETCH_LIMITS.description, placeholder: "Add a note to your sketch", "aria-label": "Sketch note", "data-test": `${dataTest}-input`, autoFocus: autoFocus, disabled: disabled, onChange: (event) => onDescriptionChange(event.target.value), onKeyDown: onKeyDown }), _jsx("div", { className: "sketch__prompt-footer", children: _jsx(Button, { variant: "primary", size: "compact", type: "button", "data-test": `${dataTest}-done`, disabled: disabled || saving, onClick: () => { void onDone(); }, children: saving ? "Saving…" : "Done" }) })] }));
|
|
18
25
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
export function SketchThumbnail({ item }) {
|
|
4
|
+
const [url, setUrl] = useState(null);
|
|
5
|
+
const hasMarks = item.document.strokes.length > 0 || (item.document.annotations?.length ?? 0) > 0;
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (!hasMarks) {
|
|
8
|
+
setUrl(null);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function")
|
|
12
|
+
return;
|
|
13
|
+
const next = URL.createObjectURL(item.document.annotatedImage);
|
|
14
|
+
setUrl(next);
|
|
15
|
+
return () => URL.revokeObjectURL(next);
|
|
16
|
+
}, [hasMarks, item.document.annotatedImage]);
|
|
17
|
+
return hasMarks && url ? (_jsx("img", { className: "sketch-thumbnail", src: url, alt: "", "aria-hidden": "true" })) : (_jsx("span", { className: "sketch-thumbnail sketch-thumbnail--empty", "aria-hidden": "true" }));
|
|
18
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import { beginSketchCapture } from "./interaction.js";
|
|
3
2
|
import { type SketchLiveDraft } from "./SketchOverlay.js";
|
|
4
3
|
import type { CapturedSketch } from "./capture.js";
|
|
5
4
|
import type { SketchStroke } from "./model.js";
|
|
@@ -9,5 +8,5 @@ export interface SketchWorkspaceProps {
|
|
|
9
8
|
}
|
|
10
9
|
export declare function projectLiveStrokes(draft: SketchLiveDraft, captured: CapturedSketch): SketchStroke[];
|
|
11
10
|
export declare function SketchWorkspace({ hostElement, projectId }: SketchWorkspaceProps): ReactElement;
|
|
12
|
-
export declare function startSketchCapture(hostElement: HTMLElement | null
|
|
11
|
+
export declare function startSketchCapture(hostElement: HTMLElement | null): void;
|
|
13
12
|
export declare function editSketch(sketchId: string): void;
|