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,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
3
|
import { CANVAS_RENDERER_ATTR } from "./roleDetection.js";
|
|
4
|
-
import { removeCanvasCard,
|
|
5
|
-
import { IconRefresh,
|
|
6
|
-
import { PROTOCOL_VERSION,
|
|
4
|
+
import { removeCanvasCard, updateCardTitle, updateCardUrl, resizeCard, setCardPosition, selectCard, getSelectedCardId, useSelectedCardId, useFocusedCardId, useBoardCamera } from "./canvasStore.js";
|
|
5
|
+
import { IconRefresh, IconArrowsDiagonal, IconCornerLeftDown } from "@tabler/icons-react";
|
|
6
|
+
import { PROTOCOL_VERSION, } from "./frameProtocol.js";
|
|
7
7
|
import { registerCardFrame, registerCardFrameSource, unregisterCardFrame, sendProjectionToCard, invalidateCanvasPreviewDocument, PROJECT_ID, WORKSPACE_ID } from "./projection.js";
|
|
8
|
-
import { IconButton } from "../ui/IconButton.js";
|
|
9
8
|
import { Button } from "../ui/Button.js";
|
|
10
9
|
import { setSelectedElement } from "../selection/selectionStore.js";
|
|
11
10
|
import { clearCanvasStructuralProjectionReports } from "../projection/structuralProjection.js";
|
|
@@ -15,11 +14,36 @@ import { getCanvasToolbarScale } from "./toolbarScale.js";
|
|
|
15
14
|
import { getCanvasResizeHandleScale } from "./resizeHandleScale.js";
|
|
16
15
|
import { disposeBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
|
|
17
16
|
import { releaseDocumentProjection } from "../projection/structuralProjection.js";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
import { startClipboardHandoffController } from "../prompt/clipboardHandoff.js";
|
|
18
|
+
import { disposeInlineTextEdit } from "../inline-text/inlineTextEditor.js";
|
|
19
|
+
import { configureNudgeUiRuntime, getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
|
|
20
|
+
import { reconcileRuntimeWithDocumentStylesheets } from "../runtime/documentStylesheetOrder.js";
|
|
21
|
+
import { subscribeCanvasRendererMessages } from "./rendererMessageRouter.js";
|
|
22
|
+
import { SketchFrameOverlay } from "../sketch/SketchFrameOverlay.js";
|
|
23
|
+
import { getCanvasResizeEdges, resizeCanvasRect, } from "./canvasResize.js";
|
|
24
|
+
const RESIZE_HANDLES = [
|
|
25
|
+
{ direction: "top-left", cursor: "nwse-resize", transformOrigin: "left top", label: "Resize card from the top-left corner" },
|
|
26
|
+
{ direction: "top", cursor: "ns-resize", transformOrigin: "center top", label: "Resize card from the top edge" },
|
|
27
|
+
{ direction: "top-right", cursor: "nesw-resize", transformOrigin: "right top", label: "Resize card from the top-right corner" },
|
|
28
|
+
{ direction: "right", cursor: "ew-resize", transformOrigin: "right center", label: "Resize card from the right edge" },
|
|
29
|
+
{ direction: "bottom-right", cursor: "nwse-resize", transformOrigin: "right bottom", label: "Resize card from the bottom-right corner" },
|
|
30
|
+
{ direction: "bottom", cursor: "ns-resize", transformOrigin: "center bottom", label: "Resize card from the bottom edge" },
|
|
31
|
+
{ direction: "bottom-left", cursor: "nesw-resize", transformOrigin: "left bottom", label: "Resize card from the bottom-left corner" },
|
|
32
|
+
{ direction: "left", cursor: "ew-resize", transformOrigin: "left center", label: "Resize card from the left edge" },
|
|
33
|
+
];
|
|
34
|
+
function resizeHandleTransform(direction, scale) {
|
|
35
|
+
if (direction === "top" || direction === "bottom")
|
|
36
|
+
return `scale(1, ${scale})`;
|
|
37
|
+
if (direction === "left" || direction === "right")
|
|
38
|
+
return `scale(${scale}, 1)`;
|
|
39
|
+
return `scale(${scale})`;
|
|
40
|
+
}
|
|
41
|
+
export function CanvasCard({ card, presentation = "canvas", presentationCard = true, onShowFocus, documentOwner }) {
|
|
21
42
|
const iframeRef = useRef(null);
|
|
43
|
+
const initialUrlRef = useRef(card.navigationUrl ?? card.url);
|
|
44
|
+
const navigationUrlRef = useRef(card.navigationUrl);
|
|
22
45
|
const documentSessionRef = useRef(null);
|
|
46
|
+
const frameRuntimeRef = useRef(null);
|
|
23
47
|
const [loadState, setLoadState] = useState("loading");
|
|
24
48
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
25
49
|
const camera = useBoardCamera();
|
|
@@ -27,12 +51,25 @@ export function CanvasCard({ card, onEdit, documentOwner }) {
|
|
|
27
51
|
const focusedCardId = useFocusedCardId();
|
|
28
52
|
const isSelected = selectedCardId === card.id;
|
|
29
53
|
const isFocused = focusedCardId === card.id;
|
|
54
|
+
const isFocusPresentationCard = presentation === "focus" && presentationCard;
|
|
30
55
|
const toolbarScale = getCanvasToolbarScale(camera.zoom);
|
|
31
56
|
const resizeHandleScale = getCanvasResizeHandleScale(camera.zoom);
|
|
32
57
|
const disposeDocumentSession = useCallback(() => {
|
|
33
58
|
documentSessionRef.current?.session.dispose();
|
|
34
59
|
documentSessionRef.current = null;
|
|
35
60
|
}, []);
|
|
61
|
+
const loadFrame = useCallback((url) => {
|
|
62
|
+
const iframe = iframeRef.current;
|
|
63
|
+
if (!iframe)
|
|
64
|
+
return;
|
|
65
|
+
if (getSelectedCardId() === card.id)
|
|
66
|
+
setSelectedElement(null);
|
|
67
|
+
disposeDocumentSession();
|
|
68
|
+
invalidateCanvasPreviewDocument(card.id);
|
|
69
|
+
setLoadState("loading");
|
|
70
|
+
setErrorMessage(null);
|
|
71
|
+
iframe.setAttribute("src", url ?? iframe.src);
|
|
72
|
+
}, [card.id, disposeDocumentSession]);
|
|
36
73
|
const bindDocumentSession = useCallback(() => {
|
|
37
74
|
const frameDocument = iframeRef.current?.contentDocument;
|
|
38
75
|
if (!documentOwner || !frameDocument)
|
|
@@ -43,6 +80,8 @@ export function CanvasCard({ card, onEdit, documentOwner }) {
|
|
|
43
80
|
const session = documentOwner.createDocumentSession(frameDocument);
|
|
44
81
|
session.registerCleanup(() => disposeBrowserCssInspection(frameDocument));
|
|
45
82
|
session.registerCleanup(() => releaseDocumentProjection(frameDocument));
|
|
83
|
+
session.registerCleanup(() => disposeInlineTextEdit("frame-disposed", frameDocument));
|
|
84
|
+
session.registerCleanup(startClipboardHandoffController(frameDocument));
|
|
46
85
|
documentSessionRef.current = { document: frameDocument, session };
|
|
47
86
|
}, [disposeDocumentSession, documentOwner]);
|
|
48
87
|
useEffect(() => {
|
|
@@ -57,27 +96,20 @@ export function CanvasCard({ card, onEdit, documentOwner }) {
|
|
|
57
96
|
};
|
|
58
97
|
}, [bindDocumentSession, card.id, disposeDocumentSession]);
|
|
59
98
|
function handleReload() {
|
|
60
|
-
|
|
61
|
-
if (getSelectedCardId() === card.id)
|
|
62
|
-
setSelectedElement(null);
|
|
63
|
-
disposeDocumentSession();
|
|
64
|
-
invalidateCanvasPreviewDocument(card.id);
|
|
65
|
-
setLoadState("loading");
|
|
66
|
-
setErrorMessage(null);
|
|
67
|
-
const currentSrc = iframeRef.current.src;
|
|
68
|
-
iframeRef.current.setAttribute("src", currentSrc);
|
|
69
|
-
}
|
|
99
|
+
loadFrame();
|
|
70
100
|
}
|
|
71
101
|
function handleRemove() {
|
|
72
102
|
if (getSelectedCardId() === card.id)
|
|
73
103
|
setSelectedElement(null);
|
|
74
104
|
removeCanvasCard(card.id);
|
|
75
105
|
}
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
106
|
+
function updateFrameTitle(title) {
|
|
107
|
+
if (!title)
|
|
108
|
+
return;
|
|
109
|
+
const runtime = getNudgeUiRuntimeConfig();
|
|
110
|
+
if (runtime.demo === true && (runtime.demoCardLabels?.length ?? 0) > 0)
|
|
111
|
+
return;
|
|
112
|
+
updateCardTitle(card.id, title);
|
|
81
113
|
}
|
|
82
114
|
/** The controller half of the handshake: announce workspace identity. */
|
|
83
115
|
function sendParentReady() {
|
|
@@ -89,39 +121,44 @@ export function CanvasCard({ card, onEdit, documentOwner }) {
|
|
|
89
121
|
cardId: card.id,
|
|
90
122
|
}, window.location.origin);
|
|
91
123
|
}
|
|
124
|
+
function adoptFrameRuntime(runtime) {
|
|
125
|
+
frameRuntimeRef.current = runtime;
|
|
126
|
+
if (getSelectedCardId() !== card.id)
|
|
127
|
+
return;
|
|
128
|
+
const frameDocument = iframeRef.current?.contentDocument;
|
|
129
|
+
if (!frameDocument)
|
|
130
|
+
return;
|
|
131
|
+
try {
|
|
132
|
+
configureNudgeUiRuntime(runtime);
|
|
133
|
+
const configured = getNudgeUiRuntimeConfig();
|
|
134
|
+
const reconciled = reconcileRuntimeWithDocumentStylesheets(configured, frameDocument);
|
|
135
|
+
if (reconciled !== configured)
|
|
136
|
+
configureNudgeUiRuntime(reconciled);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Ignore malformed same-origin renderer metadata and keep the last valid runtime.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
92
142
|
useEffect(() => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (
|
|
143
|
+
if (!isSelected || !frameRuntimeRef.current)
|
|
144
|
+
return;
|
|
145
|
+
adoptFrameRuntime(frameRuntimeRef.current);
|
|
146
|
+
}, [isSelected]);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
return subscribeCanvasRendererMessages(({ cardId, message: data }) => {
|
|
149
|
+
if (cardId !== card.id)
|
|
100
150
|
return;
|
|
101
151
|
// A renderer whose runtime finished booting after the iframe load event
|
|
102
152
|
// asks for the identity announcement it may have missed.
|
|
103
|
-
|
|
104
|
-
if (msg.type === "renderer-hello") {
|
|
105
|
-
// SAFETY: The `type === "renderer-hello"` branch above selects exactly the hello payload shape.
|
|
106
|
-
const hello = msg;
|
|
107
|
-
if (hello.protocolVersion !== PROTOCOL_VERSION)
|
|
108
|
-
return;
|
|
153
|
+
if (data.type === "renderer-hello") {
|
|
109
154
|
sendParentReady();
|
|
110
155
|
return;
|
|
111
156
|
}
|
|
112
|
-
if (!isRendererMessageFor(msg, {
|
|
113
|
-
projectId: PROJECT_ID,
|
|
114
|
-
workspaceId: WORKSPACE_ID,
|
|
115
|
-
cardId: card.id,
|
|
116
|
-
}))
|
|
117
|
-
return;
|
|
118
|
-
// SAFETY: isRendererMessageFor validated the frame identity and message shape above.
|
|
119
|
-
const data = msg;
|
|
120
157
|
if (data.type === "frame-ready") {
|
|
158
|
+
adoptFrameRuntime(data.runtime);
|
|
121
159
|
setLoadState("ready");
|
|
122
160
|
setErrorMessage(null);
|
|
123
|
-
|
|
124
|
-
updateCardTitle(card.id, data.title);
|
|
161
|
+
updateFrameTitle(data.title);
|
|
125
162
|
if (data.url)
|
|
126
163
|
updateCardUrl(card.id, data.url);
|
|
127
164
|
if (iframeRef.current) {
|
|
@@ -130,9 +167,12 @@ export function CanvasCard({ card, onEdit, documentOwner }) {
|
|
|
130
167
|
}
|
|
131
168
|
return;
|
|
132
169
|
}
|
|
170
|
+
if (data.type === "frame-runtime") {
|
|
171
|
+
adoptFrameRuntime(data.runtime);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
133
174
|
if (data.type === "frame-metadata") {
|
|
134
|
-
|
|
135
|
-
updateCardTitle(card.id, data.title);
|
|
175
|
+
updateFrameTitle(data.title);
|
|
136
176
|
if (data.url)
|
|
137
177
|
updateCardUrl(card.id, data.url);
|
|
138
178
|
return;
|
|
@@ -140,14 +180,15 @@ export function CanvasCard({ card, onEdit, documentOwner }) {
|
|
|
140
180
|
if (data.type === "frame-error") {
|
|
141
181
|
setLoadState("error");
|
|
142
182
|
setErrorMessage(data.message || "Frame failed to load");
|
|
143
|
-
return;
|
|
144
183
|
}
|
|
145
|
-
}
|
|
146
|
-
window.addEventListener("message", onMessage);
|
|
147
|
-
return () => {
|
|
148
|
-
window.removeEventListener("message", onMessage);
|
|
149
|
-
};
|
|
184
|
+
});
|
|
150
185
|
}, [card.id]);
|
|
186
|
+
useLayoutEffect(() => {
|
|
187
|
+
if (!card.navigationUrl || navigationUrlRef.current === card.navigationUrl)
|
|
188
|
+
return;
|
|
189
|
+
navigationUrlRef.current = card.navigationUrl;
|
|
190
|
+
loadFrame(card.navigationUrl);
|
|
191
|
+
}, [card.navigationUrl, loadFrame]);
|
|
151
192
|
useEffect(() => {
|
|
152
193
|
if (loadState !== "loading")
|
|
153
194
|
return;
|
|
@@ -182,6 +223,10 @@ export function CanvasCard({ card, onEdit, documentOwner }) {
|
|
|
182
223
|
}, [bindDocumentSession, card.id, disposeDocumentSession]);
|
|
183
224
|
const [isDragging, setIsDragging] = useState(false);
|
|
184
225
|
const dragRef = useRef({ startX: 0, startY: 0, cardX: 0, cardY: 0 });
|
|
226
|
+
const resizeCleanupRef = useRef(null);
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
return () => resizeCleanupRef.current?.();
|
|
229
|
+
}, []);
|
|
185
230
|
const handleToolbarPointerDown = useCallback((e) => {
|
|
186
231
|
// SAFETY: pointer events on the card target are HTMLElements in this DOM context.
|
|
187
232
|
const target = e.target;
|
|
@@ -219,57 +264,102 @@ export function CanvasCard({ card, onEdit, documentOwner }) {
|
|
|
219
264
|
setSelectedElement(null);
|
|
220
265
|
selectCard(card.id);
|
|
221
266
|
}, [card.id]);
|
|
222
|
-
const handleResizeStart = useCallback((e) => {
|
|
267
|
+
const handleResizeStart = useCallback((e, direction) => {
|
|
223
268
|
e.stopPropagation();
|
|
224
269
|
e.preventDefault();
|
|
270
|
+
resizeCleanupRef.current?.();
|
|
271
|
+
const target = e.currentTarget;
|
|
272
|
+
const pointerId = e.pointerId;
|
|
225
273
|
const startX = e.clientX;
|
|
226
274
|
const startY = e.clientY;
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
275
|
+
const start = {
|
|
276
|
+
x: card.x,
|
|
277
|
+
y: card.y,
|
|
278
|
+
width: card.width,
|
|
279
|
+
height: card.height,
|
|
280
|
+
};
|
|
231
281
|
function onMove(ev) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
282
|
+
if (ev.pointerId !== pointerId)
|
|
283
|
+
return;
|
|
284
|
+
const delta = {
|
|
285
|
+
x: (ev.clientX - startX) / camera.zoom,
|
|
286
|
+
y: (ev.clientY - startY) / camera.zoom,
|
|
287
|
+
};
|
|
288
|
+
const next = resizeCanvasRect(start, direction, delta);
|
|
289
|
+
resizeCard(card.id, next.width, next.height, { x: next.x, y: next.y });
|
|
237
290
|
}
|
|
238
|
-
function
|
|
291
|
+
function cleanup() {
|
|
239
292
|
window.removeEventListener("pointermove", onMove);
|
|
240
293
|
window.removeEventListener("pointerup", onUp);
|
|
294
|
+
window.removeEventListener("pointercancel", onCancel);
|
|
295
|
+
target.removeEventListener("lostpointercapture", onLostPointerCapture);
|
|
296
|
+
if (target.hasPointerCapture?.(pointerId))
|
|
297
|
+
target.releasePointerCapture(pointerId);
|
|
298
|
+
if (resizeCleanupRef.current === cleanup)
|
|
299
|
+
resizeCleanupRef.current = null;
|
|
300
|
+
}
|
|
301
|
+
function onUp(ev) {
|
|
302
|
+
if (ev.pointerId !== pointerId)
|
|
303
|
+
return;
|
|
304
|
+
cleanup();
|
|
305
|
+
}
|
|
306
|
+
function onCancel(ev) {
|
|
307
|
+
if (ev.pointerId !== pointerId)
|
|
308
|
+
return;
|
|
309
|
+
cleanup();
|
|
310
|
+
}
|
|
311
|
+
function onLostPointerCapture(ev) {
|
|
312
|
+
if (ev.pointerId !== pointerId)
|
|
313
|
+
return;
|
|
314
|
+
cleanup();
|
|
241
315
|
}
|
|
316
|
+
resizeCleanupRef.current = cleanup;
|
|
317
|
+
// SAFETY: the resize handle is an HTMLDivElement in this DOM context.
|
|
318
|
+
target.setPointerCapture?.(pointerId);
|
|
242
319
|
window.addEventListener("pointermove", onMove);
|
|
243
320
|
window.addEventListener("pointerup", onUp);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
else if (event.key === "ArrowUp")
|
|
254
|
-
resizeCard(card.id, card.width, Math.max(MIN_CARD_HEIGHT, card.height - step));
|
|
255
|
-
else
|
|
321
|
+
window.addEventListener("pointercancel", onCancel);
|
|
322
|
+
target.addEventListener("lostpointercapture", onLostPointerCapture);
|
|
323
|
+
}, [card, camera.zoom]);
|
|
324
|
+
function handleResizeKeyDown(event, direction) {
|
|
325
|
+
const isArrowKey = event.key === "ArrowRight"
|
|
326
|
+
|| event.key === "ArrowLeft"
|
|
327
|
+
|| event.key === "ArrowDown"
|
|
328
|
+
|| event.key === "ArrowUp";
|
|
329
|
+
if (!isArrowKey)
|
|
256
330
|
return;
|
|
257
331
|
event.preventDefault();
|
|
332
|
+
event.stopPropagation();
|
|
333
|
+
const step = event.shiftKey ? 40 : 20;
|
|
334
|
+
const delta = {
|
|
335
|
+
x: event.key === "ArrowRight" ? step : event.key === "ArrowLeft" ? -step : 0,
|
|
336
|
+
y: event.key === "ArrowDown" ? step : event.key === "ArrowUp" ? -step : 0,
|
|
337
|
+
};
|
|
338
|
+
const edges = getCanvasResizeEdges(direction);
|
|
339
|
+
if ((delta.x !== 0 && edges.horizontal === null) || (delta.y !== 0 && edges.vertical === null))
|
|
340
|
+
return;
|
|
341
|
+
const next = resizeCanvasRect(card, direction, delta);
|
|
342
|
+
resizeCard(card.id, next.width, next.height, { x: next.x, y: next.y });
|
|
258
343
|
}
|
|
259
|
-
return (_jsxs("div", { className: `canvas-card${isDragging ? " is-dragging" : ""}${isSelected ? " is-selected" : ""}${isFocused ? " is-focused" : ""}`, "data-card-id": card.id, style: {
|
|
344
|
+
return (_jsxs("div", { className: `canvas-card${isDragging ? " is-dragging" : ""}${isSelected ? " is-selected" : ""}${isFocused ? " is-focused" : ""}${isFocusPresentationCard ? " is-focus-presentation" : ""}${presentationCard ? " is-presentation-card" : ""}`, "data-card-id": card.id, style: {
|
|
260
345
|
position: "absolute",
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
346
|
+
visibility: presentation === "focus" && !presentationCard ? "hidden" : undefined,
|
|
347
|
+
pointerEvents: presentation === "focus" && !presentationCard ? "none" : undefined,
|
|
348
|
+
left: isFocusPresentationCard ? 0 : card.x,
|
|
349
|
+
top: isFocusPresentationCard ? 0 : card.y,
|
|
350
|
+
width: isFocusPresentationCard ? "100%" : card.width,
|
|
351
|
+
height: isFocusPresentationCard ? "100%" : card.height,
|
|
352
|
+
}, onPointerDown: presentation === "canvas" ? handleCardPointerDown : undefined, children: [presentation === "canvas" ? _jsx("div", { className: "canvas-card__toolbar", onPointerDown: handleToolbarPointerDown, children: _jsxs("div", { className: "canvas-card__drag-surface", "data-test": `canvas-card-drag-${card.id}`, children: [onShowFocus ? (_jsxs(Button, { title: "Focus", variant: "primary", size: "default", "data-test": `canvas-card-focus-${card.id}`, style: {
|
|
266
353
|
transform: `scale(${toolbarScale})`,
|
|
267
354
|
transformOrigin: "left bottom",
|
|
268
|
-
},
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
355
|
+
}, onClick: () => onShowFocus(card.id), children: [_jsx(IconCornerLeftDown, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), "Focus"] })) : null, _jsx("span", { className: "canvas-card__dimensions", "data-test": `canvas-card-dimensions-${card.id}`, style: {
|
|
356
|
+
transform: `translateX(-50%) scale(${toolbarScale})`,
|
|
357
|
+
transformOrigin: "center bottom",
|
|
358
|
+
}, children: card.title || `${Math.round(card.width)} × ${Math.round(card.height)} px` })] }) }) : null, _jsxs("div", { className: "canvas-card__frame", children: [loadState === "loading" ? (_jsx("div", { className: "canvas-card__loading", "data-test": `canvas-card-loading-${card.id}`, children: "Loading preview..." })) : loadState === "error" ? (_jsxs("div", { className: "canvas-card__error", "data-test": `canvas-card-error-${card.id}`, children: [_jsx("p", { children: errorMessage || "Frame could not be loaded" }), _jsxs("div", { className: "canvas-card__error-actions", children: [_jsx(Button, { size: "compact", variant: "secondary", onClick: handleReload, children: "Retry" }), _jsx(Button, { size: "compact", variant: "danger", onClick: handleRemove, children: "Remove" })] })] })) : null, _jsx("iframe", { ref: iframeRef, className: "canvas-card__iframe", src: initialUrlRef.current, title: card.title || card.url, [CANVAS_RENDERER_ATTR]: "", sandbox: "allow-scripts allow-same-origin allow-forms allow-popups", "data-test": `canvas-card-iframe-${card.id}` }), _jsx(SketchFrameOverlay, { iframe: iframeRef.current, cardUrl: card.url, ready: loadState === "ready" })] }), presentation === "canvas" ? RESIZE_HANDLES.map((handle) => (_jsx("div", { className: `canvas-card__resize-handle canvas-card__resize-handle--${handle.direction}`, "data-test": `canvas-card-resize-${card.id}${handle.direction === "bottom-right" ? "" : `-${handle.direction}`}`, "data-resize-direction": handle.direction, style: {
|
|
359
|
+
// Keep the edge's long axis aligned with the zoomed card. Only
|
|
360
|
+
// the thickness needs inverse scaling to remain screen-sized.
|
|
361
|
+
transform: resizeHandleTransform(handle.direction, resizeHandleScale),
|
|
362
|
+
transformOrigin: handle.transformOrigin,
|
|
363
|
+
cursor: handle.cursor,
|
|
364
|
+
}, onPointerDown: (event) => handleResizeStart(event, handle.direction), onKeyDown: (event) => handleResizeKeyDown(event, handle.direction), role: "button", "aria-label": handle.direction === "bottom-right" ? "Resize card" : handle.label, tabIndex: 0, children: handle.direction === "bottom-right" ? _jsx(IconArrowsDiagonal, { size: 12, "aria-hidden": "true" }) : null }, handle.direction))) : null] }));
|
|
275
365
|
}
|