nudge-ui 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/nudge-ui.mjs +3 -1
- package/dist/cli/agentCommand.d.ts +8 -0
- package/dist/cli/agentCommand.js +59 -0
- package/dist/client.mjs +878 -11
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
- package/dist/hosts/astro/clientTransport.js +63 -11
- package/dist/hosts/astro/integration.d.ts +2 -2
- package/dist/hosts/astro/integration.js +4 -3
- package/dist/hosts/environment.d.ts +15 -0
- package/dist/hosts/environment.js +16 -0
- package/dist/hosts/next/sidecar.d.ts +4 -0
- package/dist/hosts/next/sidecar.js +56 -7
- package/dist/hosts/next/wrapper.d.ts +2 -0
- package/dist/hosts/next/wrapper.js +32 -3
- package/dist/hosts/projectBridge.d.ts +21 -0
- package/dist/hosts/projectBridge.js +98 -0
- package/dist/hosts/static/cli.d.ts +1 -1
- package/dist/hosts/static/cli.js +6 -1
- package/dist/hosts/static/manifest.d.ts +1 -1
- package/dist/hosts/static/manifest.js +3 -1
- package/dist/hosts/static/server.js +45 -3
- package/dist/hosts/vite/vite.d.ts +6 -1
- package/dist/hosts/vite/vite.js +82 -15
- package/dist/inspector/agent/McpConnectionDialog.d.ts +5 -4
- package/dist/inspector/agent/McpConnectionDialog.js +20 -37
- package/dist/inspector/agent/client.d.ts +16 -3
- package/dist/inspector/agent/client.js +114 -11
- package/dist/inspector/agent/connectionStatus.d.ts +3 -3
- package/dist/inspector/agent/connectionStatus.js +11 -4
- package/dist/inspector/agent/httpTransport.d.ts +8 -0
- package/dist/inspector/agent/httpTransport.js +78 -18
- package/dist/inspector/agent/protocol.d.ts +11 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +1 -1
- package/dist/inspector/agent/verification.js +69 -12
- package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
- package/dist/inspector/canvas/CanvasCard.js +181 -91
- package/dist/inspector/canvas/CanvasElementOverlay.js +288 -52
- package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
- package/dist/inspector/canvas/CanvasToolbar.js +24 -0
- package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +410 -72
- package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
- package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
- package/dist/inspector/canvas/canvasResize.d.ts +30 -0
- package/dist/inspector/canvas/canvasResize.js +52 -0
- package/dist/inspector/canvas/canvasStore.d.ts +30 -6
- package/dist/inspector/canvas/canvasStore.js +128 -61
- package/dist/inspector/canvas/frameProtocol.d.ts +82 -5
- package/dist/inspector/canvas/frameProtocol.js +63 -2
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -0
- package/dist/inspector/canvas/keyboardShortcuts.js +16 -0
- package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
- package/dist/inspector/canvas/linkEligibility.js +0 -29
- package/dist/inspector/canvas/projection.d.ts +7 -0
- package/dist/inspector/canvas/projection.js +118 -25
- package/dist/inspector/canvas/rendererBootstrap.js +42 -29
- package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
- package/dist/inspector/canvas/rendererElementSelector.js +385 -58
- package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
- package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
- package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
- package/dist/inspector/canvas/sessionStore.d.ts +1 -0
- package/dist/inspector/canvas/sessionStore.js +12 -4
- package/dist/inspector/canvas/staleChangeDetector.js +16 -14
- package/dist/inspector/changes/changesLog.js +31 -1
- package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
- package/dist/inspector/changes/managedStyleProjection.js +3 -3
- package/dist/inspector/changes/structuralTypes.d.ts +4 -0
- package/dist/inspector/changes/workspaceChanges.d.ts +4 -0
- package/dist/inspector/changes/workspaceChanges.js +29 -3
- package/dist/inspector/clientManifest.d.ts +7 -0
- package/dist/inspector/clientManifest.js +12 -0
- package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
- package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
- package/dist/inspector/index.d.ts +2 -0
- package/dist/inspector/index.js +80 -32
- package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
- package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
- package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
- package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
- package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
- package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
- package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
- package/dist/inspector/inline-text/textEditChange.js +165 -0
- package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
- package/dist/inspector/inspection/selectionProperty.js +1 -0
- package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
- package/dist/inspector/overlay/DomNavigator.js +40 -0
- package/dist/inspector/overlay/InspectorOverlay.js +6 -2
- package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
- package/dist/inspector/overlay/clickPolicy.js +34 -3
- package/dist/inspector/overlay/elementSelector.d.ts +1 -1
- package/dist/inspector/overlay/elementSelector.js +18 -7
- package/dist/inspector/overlay/selectedGeometry.js +21 -2
- package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
- package/dist/inspector/overlay/spacingGestures.js +351 -0
- package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
- package/dist/inspector/overlay/structuralGestures.js +12 -10
- package/dist/inspector/projection/managedStylesheet.js +4 -1
- package/dist/inspector/projection/renderedInstance.d.ts +2 -0
- package/dist/inspector/projection/renderedInstance.js +5 -1
- package/dist/inspector/projection/structuralProjection.d.ts +2 -0
- package/dist/inspector/projection/structuralProjection.js +85 -1
- package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
- package/dist/inspector/projection/textProjection.d.ts +2 -0
- package/dist/inspector/projection/textProjection.js +10 -4
- package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
- package/dist/inspector/prompt/clipboardHandoff.js +3 -3
- package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
- package/dist/inspector/prompt/generatePrompt.js +19 -2
- package/dist/inspector/runtime/editorShell.d.ts +2 -0
- package/dist/inspector/runtime/editorShell.js +4 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +5 -1
- package/dist/inspector/runtime/runtimeConfig.js +4 -0
- package/dist/inspector/selection/hierarchy.d.ts +6 -0
- package/dist/inspector/selection/hierarchy.js +8 -0
- package/dist/inspector/selection/selectionStore.js +2 -0
- package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
- package/dist/inspector/settings/SettingsDialog.js +12 -2
- package/dist/inspector/shell/AppShell.js +5 -1
- package/dist/inspector/shell/ChangesLog.js +26 -22
- package/dist/inspector/shell/CopyPromptButton.js +102 -19
- package/dist/inspector/shell/EmptyState.js +1 -1
- package/dist/inspector/shell/InspectorShell.d.ts +0 -1
- package/dist/inspector/shell/InspectorShell.js +38 -42
- package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
- package/dist/inspector/shell/PeekOriginalButton.js +57 -0
- package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
- package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
- package/dist/inspector/shell/originalPreview.d.ts +9 -0
- package/dist/inspector/shell/originalPreview.js +61 -0
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +15 -0
- package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
- package/dist/inspector/sketch/SketchChanges.js +72 -0
- package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
- package/dist/inspector/sketch/SketchEditor.js +402 -0
- package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
- package/dist/inspector/sketch/SketchEntryButton.js +11 -0
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
- package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
- package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
- package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
- package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
- package/dist/inspector/sketch/SketchOverlay.js +420 -0
- package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
- package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
- package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
- package/dist/inspector/sketch/SketchWorkspace.js +152 -0
- package/dist/inspector/sketch/capture.d.ts +55 -0
- package/dist/inspector/sketch/capture.js +350 -0
- package/dist/inspector/sketch/freehand.d.ts +13 -0
- package/dist/inspector/sketch/freehand.js +64 -0
- package/dist/inspector/sketch/geometry.d.ts +17 -0
- package/dist/inspector/sketch/geometry.js +98 -0
- package/dist/inspector/sketch/handoff.d.ts +21 -0
- package/dist/inspector/sketch/handoff.js +72 -0
- package/dist/inspector/sketch/interaction.d.ts +21 -0
- package/dist/inspector/sketch/interaction.js +98 -0
- package/dist/inspector/sketch/model.d.ts +104 -0
- package/dist/inspector/sketch/model.js +282 -0
- package/dist/inspector/sketch/persistence.d.ts +21 -0
- package/dist/inspector/sketch/persistence.js +205 -0
- package/dist/inspector/sketch/raster.d.ts +22 -0
- package/dist/inspector/sketch/raster.js +203 -0
- package/dist/inspector/sketch/sketchNote.d.ts +10 -0
- package/dist/inspector/sketch/sketchNote.js +30 -0
- package/dist/inspector/sketch/store.d.ts +47 -0
- package/dist/inspector/sketch/store.js +434 -0
- package/dist/inspector/styleEditors/BorderEditor.js +15 -19
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
- package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
- package/dist/inspector/styleEditors/ColorPicker.js +11 -36
- package/dist/inspector/styleEditors/GapField.d.ts +3 -2
- package/dist/inspector/styleEditors/GapField.js +2 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/LayoutSection.js +11 -8
- package/dist/inspector/styleEditors/SpacingBox.js +31 -33
- package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
- package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
- package/dist/inspector/styleEditors/layoutValue.js +3 -0
- package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
- package/dist/inspector/styleEditors/nudgeValue.js +93 -5
- package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
- package/dist/inspector/styleEditors/stylePresence.js +22 -0
- package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
- package/dist/inspector/styleEditors/textRelevance.js +35 -0
- package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
- package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
- package/dist/inspector/tokens/TokenField.js +130 -7
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
- package/dist/inspector/tokens/dragPointerLock.js +70 -0
- package/dist/inspector/tokens/editActions.d.ts +2 -0
- package/dist/inspector/tokens/editActions.js +2 -2
- package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
- package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
- package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
- package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
- package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
- package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
- package/dist/inspector/tokens/resolution.d.ts +3 -26
- package/dist/inspector/tokens/resolution.js +44 -735
- package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
- package/dist/inspector/tokens/tokenAvailability.js +204 -0
- package/dist/inspector/ui/Button.d.ts +1 -1
- package/dist/inspector/ui/Disclosure.d.ts +22 -0
- package/dist/inspector/ui/Disclosure.js +13 -0
- package/dist/inspector/ui/SideValuesField.js +2 -2
- package/dist/inspector/ui/StatusCallout.d.ts +4 -1
- package/dist/inspector/ui/StatusCallout.js +2 -2
- package/dist/inspector/ui/dragCursor.d.ts +8 -0
- package/dist/inspector/ui/dragCursor.js +21 -0
- package/dist/inspector/ui/styles.js +21 -17
- package/dist/internal/agent-protocol.d.ts +65 -1
- package/dist/internal/agent-protocol.js +183 -3
- package/dist/nudge-ui.mjs +276 -58
- package/dist/transport/editor.d.ts +47 -0
- package/dist/transport/editor.js +163 -0
- package/dist/transport/index.d.ts +1 -0
- package/dist/transport/index.js +1 -0
- package/dist/transport/routes.d.ts +10 -0
- package/dist/transport/routes.js +10 -0
- package/package.json +3 -1
- package/dist/inspector/shell/DomNavigation.d.ts +0 -8
- package/dist/inspector/shell/DomNavigation.js +0 -30
- package/dist/inspector/shell/panelLayout.d.ts +0 -2
- package/dist/inspector/shell/panelLayout.js +0 -29
|
@@ -4,9 +4,13 @@ import { getWorkspaceChanges } from "../changes/workspaceChanges.js";
|
|
|
4
4
|
import { getChangesList, isComponentChange, isTextContentChange, isTokenChange, reconcileVerifiedWorkspaceChanges, } from "../changes/changesLog.js";
|
|
5
5
|
import { verifyPreview } from "../projection/managedStylesheet.js";
|
|
6
6
|
import { resolveRenderedInstance } from "../projection/renderedInstance.js";
|
|
7
|
-
import { getStructuralChanges, } from "../projection/structuralProjection.js";
|
|
7
|
+
import { documentStateKey, getStructuralChanges, } from "../projection/structuralProjection.js";
|
|
8
8
|
import { resolveTextProjectionTarget, resolveTextProjectionTextNode, } from "../projection/textProjection.js";
|
|
9
9
|
import { applyHostWorkspaceProjection, compileWorkspaceProjection, } from "../projection/workspaceProjection.js";
|
|
10
|
+
import { isCanvasProjectionRevisionCurrent, projectWorkspaceSnapshotToDocument, } from "../canvas/projection.js";
|
|
11
|
+
import { getActiveCanvasDocument } from "../canvas/activeCanvasDocument.js";
|
|
12
|
+
import { getSelectedElement } from "../selection/selectionStore.js";
|
|
13
|
+
import { isEditorShellDocument } from "../runtime/editorShell.js";
|
|
10
14
|
const dispatches = new Map();
|
|
11
15
|
function stableJsonValue(value) {
|
|
12
16
|
if (Array.isArray(value))
|
|
@@ -72,7 +76,20 @@ function verifyText(change, doc) {
|
|
|
72
76
|
* verifies.
|
|
73
77
|
*/
|
|
74
78
|
function verifyStructuralDelete(change, doc) {
|
|
75
|
-
|
|
79
|
+
if (!change.route || !change.state || !doc.location)
|
|
80
|
+
return false;
|
|
81
|
+
let currentRoute;
|
|
82
|
+
try {
|
|
83
|
+
const url = new URL(doc.location.href);
|
|
84
|
+
url.hash = "";
|
|
85
|
+
currentRoute = url.href;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return currentRoute === change.route
|
|
91
|
+
&& documentStateKey(doc) === change.state
|
|
92
|
+
&& resolveRenderedInstance(doc, change.target).status === "missing";
|
|
76
93
|
}
|
|
77
94
|
/**
|
|
78
95
|
* A move verifies when the element sits at its recorded destination without
|
|
@@ -142,11 +159,26 @@ export function getAgentDispatchSize(revision) {
|
|
|
142
159
|
return 0;
|
|
143
160
|
return snapshot.changes.length + snapshot.structuralChanges.length;
|
|
144
161
|
}
|
|
145
|
-
function afterBrowserPaint() {
|
|
146
|
-
|
|
162
|
+
function afterBrowserPaint(doc) {
|
|
163
|
+
const ownerWindow = doc.defaultView;
|
|
164
|
+
const requestFrame = ownerWindow?.requestAnimationFrame.bind(ownerWindow);
|
|
165
|
+
if (!ownerWindow || !requestFrame)
|
|
147
166
|
return Promise.resolve();
|
|
148
167
|
return new Promise((resolve) => {
|
|
149
|
-
|
|
168
|
+
let settled = false;
|
|
169
|
+
const finish = () => {
|
|
170
|
+
if (settled)
|
|
171
|
+
return;
|
|
172
|
+
settled = true;
|
|
173
|
+
clearTimeout(timeout);
|
|
174
|
+
resolve();
|
|
175
|
+
};
|
|
176
|
+
const timeout = setTimeout(finish, 250);
|
|
177
|
+
requestFrame(() => {
|
|
178
|
+
if (doc.defaultView !== ownerWindow)
|
|
179
|
+
return finish();
|
|
180
|
+
requestFrame(finish);
|
|
181
|
+
});
|
|
150
182
|
});
|
|
151
183
|
}
|
|
152
184
|
/**
|
|
@@ -154,7 +186,18 @@ function afterBrowserPaint() {
|
|
|
154
186
|
* observable after removing Nudge's own preview. Newer edits and ambiguous
|
|
155
187
|
* records are preserved.
|
|
156
188
|
*/
|
|
157
|
-
export async function verifyAndReconcileHandoff(snapshot) {
|
|
189
|
+
export async function verifyAndReconcileHandoff(snapshot, doc = activeVerificationDocument()) {
|
|
190
|
+
if (!doc)
|
|
191
|
+
return 0;
|
|
192
|
+
const applyProjection = async (snapshot) => {
|
|
193
|
+
if (doc === document && !isEditorShellDocument(doc)) {
|
|
194
|
+
applyHostWorkspaceProjection(compileWorkspaceProjection(snapshot));
|
|
195
|
+
return "host";
|
|
196
|
+
}
|
|
197
|
+
if (doc === document)
|
|
198
|
+
return null;
|
|
199
|
+
return projectWorkspaceSnapshotToDocument(doc, snapshot);
|
|
200
|
+
};
|
|
158
201
|
const sentByKey = new Map(snapshot.changes.map((change) => [changeKey(change), handoffChangeFingerprint(change)]));
|
|
159
202
|
const current = getChangesList();
|
|
160
203
|
const eligible = current.filter((change) => sentByKey.get(changeKey(change)) === handoffChangeFingerprint(change));
|
|
@@ -169,14 +212,19 @@ export async function verifyAndReconcileHandoff(snapshot) {
|
|
|
169
212
|
const remainingStructural = currentStructural.filter((change) => !eligibleStructuralIds.has(change.id));
|
|
170
213
|
try {
|
|
171
214
|
const currentWorkspace = getWorkspaceChanges();
|
|
172
|
-
|
|
215
|
+
const previewRevision = await applyProjection({
|
|
173
216
|
revision: currentWorkspace.revision,
|
|
174
217
|
changes: remaining,
|
|
175
218
|
structuralChanges: remainingStructural,
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
219
|
+
});
|
|
220
|
+
if (previewRevision === null)
|
|
221
|
+
return 0;
|
|
222
|
+
await afterBrowserPaint(doc);
|
|
223
|
+
if (typeof previewRevision === "number"
|
|
224
|
+
&& !isCanvasProjectionRevisionCurrent(doc, previewRevision))
|
|
225
|
+
return 0;
|
|
226
|
+
const verified = verifiedChangeKeys(eligible, doc);
|
|
227
|
+
const verifiedStructural = verifiedStructuralChangeIds(eligibleStructural, doc);
|
|
180
228
|
// A user can make another inspector edit while the two-frame verification
|
|
181
229
|
// window is open. Recheck byte identity immediately before reconciliation
|
|
182
230
|
// so a newer value at the same key can never be removed.
|
|
@@ -184,15 +232,24 @@ export async function verifyAndReconcileHandoff(snapshot) {
|
|
|
184
232
|
const stillVerified = new Set([...verified].filter((key) => currentByKey.get(key) === sentByKey.get(key)));
|
|
185
233
|
const currentStructuralById = new Map(getStructuralChanges().map((change) => [change.id, handoffStructuralFingerprint(change)]));
|
|
186
234
|
const stillVerifiedStructural = new Set([...verifiedStructural].filter((id) => currentStructuralById.get(id) === sentStructural.get(id)));
|
|
235
|
+
if (typeof previewRevision === "number"
|
|
236
|
+
&& !isCanvasProjectionRevisionCurrent(doc, previewRevision))
|
|
237
|
+
return 0;
|
|
187
238
|
return reconcileVerifiedWorkspaceChanges(stillVerified, stillVerifiedStructural);
|
|
188
239
|
}
|
|
189
240
|
finally {
|
|
190
241
|
// Reconciliation reapplies the canonical set. If it was unable to write
|
|
191
242
|
// (for example, a read-only Canvas lease), restore the untouched set here.
|
|
192
243
|
const canonicalWorkspace = getWorkspaceChanges();
|
|
193
|
-
|
|
244
|
+
await applyProjection(canonicalWorkspace);
|
|
194
245
|
}
|
|
195
246
|
}
|
|
247
|
+
function activeVerificationDocument() {
|
|
248
|
+
const selectedDocument = getSelectedElement()?.domElement.ownerDocument;
|
|
249
|
+
if (selectedDocument && selectedDocument !== document)
|
|
250
|
+
return selectedDocument;
|
|
251
|
+
return getActiveCanvasDocument() ?? (isEditorShellDocument() ? null : document);
|
|
252
|
+
}
|
|
196
253
|
/** Reconciles the captured records for one completed connected-agent request. */
|
|
197
254
|
export async function verifyAndReconcileAgentDispatch(revision) {
|
|
198
255
|
const snapshot = dispatches.get(revision);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
|
-
import { type CanvasCard } from "./canvasStore.js";
|
|
2
|
+
import { type CanvasCard, type CanvasPresentation } from "./canvasStore.js";
|
|
3
3
|
import type { InspectorSession } from "../session/sessionFactory.js";
|
|
4
4
|
interface CanvasCardProps {
|
|
5
5
|
card: CanvasCard;
|
|
6
|
-
|
|
6
|
+
presentation?: CanvasPresentation;
|
|
7
|
+
presentationCard?: boolean;
|
|
8
|
+
onShowFocus?: (cardId: string) => void;
|
|
7
9
|
documentOwner?: InspectorSession;
|
|
8
10
|
}
|
|
9
|
-
export declare function CanvasCard({ card,
|
|
11
|
+
export declare function CanvasCard({ card, presentation, presentationCard, onShowFocus, documentOwner }: CanvasCardProps): ReactElement;
|
|
10
12
|
export {};
|
|
@@ -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
|
}
|