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,11 +1,12 @@
|
|
|
1
|
+
import { inspectorStorage } from "../runtime/inspectorStorage.js";
|
|
1
2
|
import { isFrameContent } from "./frameContent.js";
|
|
2
3
|
import { clearComments } from "../comments/store.js";
|
|
4
|
+
import { clearSketchesForProject } from "../sketch/store.js";
|
|
5
|
+
import { clearSketchClipboardHandoff } from "../sketch/handoff.js";
|
|
3
6
|
import { documentTarget, targetFromKey } from "../drafts/model.js";
|
|
4
|
-
import { canEditDraft, getActiveDraftChanges, isDraftLocked } from "../changes/draftChanges.js";
|
|
5
7
|
import { getFrameGroups, isFrameGroup, getCanvasCards, getFocusedCardId, getSelectedCardId, hydrateCanvasStore, removeCanvasCard, } from "./canvasStore.js";
|
|
6
8
|
import { getCanvasMode, getCanvasPresentation, getBoardCamera, setBoardCamera, } from "./viewStore.js";
|
|
7
9
|
import { applyRules } from "../projection/managedStylesheet.js";
|
|
8
|
-
import { clearActiveDraft } from "../changes/changesLog.js";
|
|
9
10
|
import { removeManagedSheet } from "../projection/managedStylesheet.js";
|
|
10
11
|
import { setSelectedElement } from "../selection/selectionStore.js";
|
|
11
12
|
import { canWriteWorkspace } from "./workspaceLease.js";
|
|
@@ -13,7 +14,7 @@ import { resetStructuralDeleteProjection, } from "../projection/structuralProjec
|
|
|
13
14
|
import { projectToAllReadyCards } from "./projection.js";
|
|
14
15
|
import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
|
|
15
16
|
import { clearClipboardHandoff, getClipboardHandoffSnapshot, hydrateClipboardHandoff, isClipboardHandoffSnapshot, } from "../prompt/clipboardHandoff.js";
|
|
16
|
-
import { activateDraftForCard, clearDrafts, persistDrafts } from "../drafts/store.js";
|
|
17
|
+
import { activateDraftForCard, canEditDraft, clearActiveDraft, clearDrafts, getActiveDraftChanges, isDraftLocked, persistDrafts, } from "../drafts/store.js";
|
|
17
18
|
const SCHEMA_VERSION = 17;
|
|
18
19
|
const STORAGE_PREFIX = "nudge-ui";
|
|
19
20
|
function projectId() {
|
|
@@ -79,7 +80,7 @@ function persistSessionUnchecked() {
|
|
|
79
80
|
try {
|
|
80
81
|
persistDrafts();
|
|
81
82
|
const session = buildSession();
|
|
82
|
-
|
|
83
|
+
inspectorStorage()?.setItem(storageKey(projectId()), JSON.stringify(session));
|
|
83
84
|
return true;
|
|
84
85
|
}
|
|
85
86
|
catch {
|
|
@@ -92,7 +93,7 @@ export function hydrateSession() {
|
|
|
92
93
|
return { restored: false, changeCount: 0 };
|
|
93
94
|
let raw = null;
|
|
94
95
|
try {
|
|
95
|
-
raw =
|
|
96
|
+
raw = inspectorStorage()?.getItem(storageKey(projectId())) ?? null;
|
|
96
97
|
}
|
|
97
98
|
catch {
|
|
98
99
|
return { restored: false, changeCount: 0 };
|
|
@@ -211,24 +212,26 @@ export function hydrateSession() {
|
|
|
211
212
|
}
|
|
212
213
|
function safeDiscard() {
|
|
213
214
|
try {
|
|
214
|
-
|
|
215
|
+
inspectorStorage()?.removeItem(storageKey(projectId()));
|
|
215
216
|
}
|
|
216
217
|
catch {
|
|
217
218
|
// ignore
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
|
-
/** Clears the active
|
|
221
|
+
/** Clears the active draft, its comments, and project sketches while preserving canvas geometry and other drafts. */
|
|
221
222
|
export function clearSelectedFrameChanges() {
|
|
222
223
|
if (!canEditDraft())
|
|
223
|
-
return;
|
|
224
|
+
return Promise.resolve();
|
|
224
225
|
const cardId = getSelectedCardId() ?? getFocusedCardId();
|
|
225
226
|
if (cardId)
|
|
226
227
|
activateDraftForCard(cardId);
|
|
227
228
|
clearComments(targetFromKey(getActiveDraftChanges().draftId) ?? documentTarget(window.location.href));
|
|
228
229
|
clearActiveDraft();
|
|
230
|
+
clearSketchClipboardHandoff();
|
|
229
231
|
setSelectedElement(null);
|
|
230
232
|
projectToAllReadyCards();
|
|
231
233
|
scheduleAutoSave();
|
|
234
|
+
return clearSketchesForProject();
|
|
232
235
|
}
|
|
233
236
|
export function clearSession() {
|
|
234
237
|
if (isDraftLocked())
|
|
@@ -236,7 +239,7 @@ export function clearSession() {
|
|
|
236
239
|
if (!canWriteWorkspace())
|
|
237
240
|
return;
|
|
238
241
|
try {
|
|
239
|
-
|
|
242
|
+
inspectorStorage()?.removeItem(storageKey(projectId()));
|
|
240
243
|
}
|
|
241
244
|
catch {
|
|
242
245
|
// ignore
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { draftIdForCard } from "../drafts/store.js";
|
|
2
|
-
import { isPreviewableChange, isTokenChange, } from "../changes/
|
|
1
|
+
import { draftIdForCard, getActiveDraftChanges } from "../drafts/store.js";
|
|
2
|
+
import { isPreviewableChange, isTokenChange, } from "../changes/types.js";
|
|
3
3
|
import { changeKey } from "../changes/model.js";
|
|
4
4
|
import { beginPreviewAttempt, clearPreviewDiagnostics, getHostPreviewDocument, notifyPreviewDiagnostics, publishPreviewDiagnostic, } from "../changes/previewDiagnostics.js";
|
|
5
|
-
import { getActiveDraftChanges } from "../changes/draftChanges.js";
|
|
6
5
|
import { getCanvasPreviewDocument, getRegisteredFrames } from "./projection.js";
|
|
7
|
-
import {
|
|
6
|
+
import { subscribeCanvasRendererMessages } from "./rendererMessageRouter.js";
|
|
8
7
|
import { getCanvasCards } from "./canvasStore.js";
|
|
9
8
|
import { getCanvasMode } from "./viewStore.js";
|
|
10
|
-
import { isRendererMessageFor } from "./frameProtocol.js";
|
|
11
9
|
import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
|
|
12
10
|
const VERIFICATION_TIMEOUT_MS = 5000;
|
|
13
11
|
const STALE_CHECK_DEBOUNCE_MS = 100;
|
|
@@ -15,6 +13,7 @@ let verificationTimer = null;
|
|
|
15
13
|
let debounceTimer = null;
|
|
16
14
|
let pendingChanges = null;
|
|
17
15
|
let pendingAttempts = new Map();
|
|
16
|
+
let stopFrameReady = null;
|
|
18
17
|
function checkSelectorInDocument(doc, selector) {
|
|
19
18
|
try {
|
|
20
19
|
return doc.querySelectorAll(selector).length > 0;
|
|
@@ -189,22 +188,10 @@ export function startStaleDetection(changes) {
|
|
|
189
188
|
verificationTimer = null;
|
|
190
189
|
notifyPreviewDiagnostics();
|
|
191
190
|
}, VERIFICATION_TIMEOUT_MS);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return;
|
|
197
|
-
const msg = event.data;
|
|
198
|
-
const frame = findCanvasFrameBySource(event.source);
|
|
199
|
-
if (!frame || !isRendererMessageFor(msg, {
|
|
200
|
-
projectId: PROJECT_ID,
|
|
201
|
-
workspaceId: WORKSPACE_ID,
|
|
202
|
-
cardId: frame.cardId,
|
|
203
|
-
}) || msg.type !== "frame-ready")
|
|
204
|
-
return;
|
|
205
|
-
if (!pendingChanges)
|
|
206
|
-
return;
|
|
207
|
-
scheduleStaleCheck();
|
|
191
|
+
stopFrameReady ??= subscribeCanvasRendererMessages(({ message }) => {
|
|
192
|
+
if (message.type === "frame-ready" && pendingChanges)
|
|
193
|
+
scheduleStaleCheck();
|
|
194
|
+
});
|
|
208
195
|
}
|
|
209
196
|
export function cancelStaleDetection() {
|
|
210
197
|
if (verificationTimer) {
|
|
@@ -217,7 +204,8 @@ export function cancelStaleDetection() {
|
|
|
217
204
|
}
|
|
218
205
|
pendingChanges = null;
|
|
219
206
|
pendingAttempts = new Map();
|
|
220
|
-
|
|
207
|
+
stopFrameReady?.();
|
|
208
|
+
stopFrameReady = null;
|
|
221
209
|
notifyPreviewDiagnostics();
|
|
222
210
|
}
|
|
223
211
|
export function isVerificationPending() {
|
|
@@ -32,7 +32,9 @@ export interface SerializableElementChange {
|
|
|
32
32
|
component: string;
|
|
33
33
|
};
|
|
34
34
|
runtimeEvidence?: NonNullable<ElementChangeRecord["runtimeEvidence"]>;
|
|
35
|
+
sourceEvidence?: NonNullable<ElementChangeRecord["sourceEvidence"]>;
|
|
35
36
|
scope?: EditScope;
|
|
37
|
+
sourceInstanceCount?: number;
|
|
36
38
|
instanceOverride?: RenderedInstanceOverride;
|
|
37
39
|
state?: NonNullable<ElementChangeRecord["state"]>;
|
|
38
40
|
}
|
|
@@ -16,7 +16,7 @@ export function isSerializableElementChangeValue(value) {
|
|
|
16
16
|
|| !hasOnlyKeys(value, [
|
|
17
17
|
"kind", "cid", "file", "line", "column", "selector", "property",
|
|
18
18
|
"sourceProperty", "sourceAuthoredValue", "oldToken", "newToken", "rawValue",
|
|
19
|
-
"oldRawValue", "source", "runtimeEvidence", "scope", "instanceOverride", "state",
|
|
19
|
+
"oldRawValue", "source", "runtimeEvidence", "sourceEvidence", "scope", "sourceInstanceCount", "instanceOverride", "state",
|
|
20
20
|
])
|
|
21
21
|
|| (value.kind !== undefined && value.kind !== "element")
|
|
22
22
|
|| typeof value.cid !== "string"
|
|
@@ -33,7 +33,9 @@ export function isSerializableElementChangeValue(value) {
|
|
|
33
33
|
|| (value.rawValue !== undefined && typeof value.rawValue !== "string")
|
|
34
34
|
|| (value.oldRawValue !== undefined && typeof value.oldRawValue !== "string")
|
|
35
35
|
|| (value.runtimeEvidence !== undefined && !isRuntimeElementEvidenceValue(value.runtimeEvidence))
|
|
36
|
+
|| (value.sourceEvidence !== undefined && !isSourceElementEvidenceValue(value.sourceEvidence))
|
|
36
37
|
|| !isEditScopeValue(value.scope)
|
|
38
|
+
|| (value.sourceInstanceCount !== undefined && (!Number.isSafeInteger(value.sourceInstanceCount) || value.sourceInstanceCount < 1))
|
|
37
39
|
|| (value.instanceOverride !== undefined && value.scope !== "rendered-instance")
|
|
38
40
|
|| (value.scope === "rendered-instance" && !isRenderedInstanceOverride(value.instanceOverride))
|
|
39
41
|
|| (value.state !== undefined && !isElementStateValue(value.state))) {
|
|
@@ -115,7 +117,9 @@ export function serializeElementChange(change) {
|
|
|
115
117
|
oldRawValue: change.oldRawValue,
|
|
116
118
|
source: change.source,
|
|
117
119
|
runtimeEvidence: change.runtimeEvidence ? { ...change.runtimeEvidence } : undefined,
|
|
120
|
+
sourceEvidence: change.sourceEvidence ? { ...change.sourceEvidence } : undefined,
|
|
118
121
|
scope: change.scope === "rendered-instance" ? "rendered-instance" : "source-site",
|
|
122
|
+
...(change.sourceInstanceCount !== undefined ? { sourceInstanceCount: change.sourceInstanceCount } : {}),
|
|
119
123
|
instanceOverride: change.scope === "rendered-instance" ? change.instanceOverride : undefined,
|
|
120
124
|
state: change.state,
|
|
121
125
|
};
|
|
@@ -161,6 +165,9 @@ export function serializeTextContentChange(change) {
|
|
|
161
165
|
props: change.target.props,
|
|
162
166
|
ariaLabel: change.target.ariaLabel,
|
|
163
167
|
beforeText: change.target.beforeText,
|
|
168
|
+
...(change.target.tagName !== undefined ? { tagName: change.target.tagName } : {}),
|
|
169
|
+
...(change.target.elementId !== undefined ? { elementId: change.target.elementId } : {}),
|
|
170
|
+
...(change.target.domPath !== undefined ? { domPath: change.target.domPath } : {}),
|
|
164
171
|
...(change.target.textNodePath?.length
|
|
165
172
|
? { textNodePath: [...change.target.textNodePath] }
|
|
166
173
|
: {}),
|
|
@@ -200,7 +207,9 @@ export function deserializeElementChange(serialized) {
|
|
|
200
207
|
oldRawValue: serialized.oldRawValue,
|
|
201
208
|
source: serialized.source,
|
|
202
209
|
runtimeEvidence: serialized.runtimeEvidence ? { ...serialized.runtimeEvidence } : undefined,
|
|
210
|
+
sourceEvidence: serialized.sourceEvidence ? { ...serialized.sourceEvidence } : undefined,
|
|
203
211
|
scope: serialized.scope ?? "source-site",
|
|
212
|
+
...(serialized.sourceInstanceCount !== undefined ? { sourceInstanceCount: serialized.sourceInstanceCount } : {}),
|
|
204
213
|
instanceOverride: serialized.scope === "rendered-instance" ? serialized.instanceOverride : undefined,
|
|
205
214
|
state: serialized.state,
|
|
206
215
|
};
|
|
@@ -245,6 +254,9 @@ export function deserializeTextContentChange(serialized) {
|
|
|
245
254
|
props: serialized.target.props,
|
|
246
255
|
ariaLabel: serialized.target.ariaLabel,
|
|
247
256
|
beforeText: serialized.target.beforeText,
|
|
257
|
+
...(serialized.target.tagName !== undefined ? { tagName: serialized.target.tagName } : {}),
|
|
258
|
+
...(serialized.target.elementId !== undefined ? { elementId: serialized.target.elementId } : {}),
|
|
259
|
+
...(serialized.target.domPath !== undefined ? { domPath: serialized.target.domPath } : {}),
|
|
248
260
|
...(serialized.target.textNodePath?.length
|
|
249
261
|
? { textNodePath: [...serialized.target.textNodePath] }
|
|
250
262
|
: {}),
|
|
@@ -324,12 +336,24 @@ function isComponentInvocationEvidenceValue(value) {
|
|
|
324
336
|
}
|
|
325
337
|
function isRuntimeElementEvidenceValue(value) {
|
|
326
338
|
return isRecord(value)
|
|
327
|
-
&& hasOnlyKeys(value, ["reason", "tagName", "text", "props", "ariaLabel"])
|
|
339
|
+
&& hasOnlyKeys(value, ["reason", "tagName", "text", "props", "ariaLabel", "domPath", "elementId"])
|
|
328
340
|
&& (value.reason === undefined || value.reason === "runtime-created" || value.reason === "unannotated")
|
|
329
341
|
&& typeof value.tagName === "string"
|
|
330
342
|
&& (typeof value.text === "string" || value.text === null)
|
|
331
343
|
&& (typeof value.props === "string" || value.props === null)
|
|
332
|
-
&& (typeof value.ariaLabel === "string" || value.ariaLabel === null)
|
|
344
|
+
&& (typeof value.ariaLabel === "string" || value.ariaLabel === null)
|
|
345
|
+
&& (value.domPath === undefined || typeof value.domPath === "string" || value.domPath === null)
|
|
346
|
+
&& (value.elementId === undefined || typeof value.elementId === "string" || value.elementId === null);
|
|
347
|
+
}
|
|
348
|
+
function isSourceElementEvidenceValue(value) {
|
|
349
|
+
return isRecord(value)
|
|
350
|
+
&& hasOnlyKeys(value, ["tagName", "props", "text", "ariaLabel", "domPath", "elementId"])
|
|
351
|
+
&& typeof value.tagName === "string"
|
|
352
|
+
&& (typeof value.props === "string" || value.props === null)
|
|
353
|
+
&& (value.text === undefined || typeof value.text === "string" || value.text === null)
|
|
354
|
+
&& (value.ariaLabel === undefined || typeof value.ariaLabel === "string" || value.ariaLabel === null)
|
|
355
|
+
&& (value.domPath === undefined || typeof value.domPath === "string" || value.domPath === null)
|
|
356
|
+
&& (value.elementId === undefined || typeof value.elementId === "string" || value.elementId === null);
|
|
333
357
|
}
|
|
334
358
|
function isTokenRefValue(value) {
|
|
335
359
|
if (value === null)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DraftChangesSnapshot, DraftProjectionEvent } from "../drafts/draftStore.js";
|
|
2
|
+
/** The host document's preview of the active draft. */
|
|
3
|
+
export interface DraftPreview {
|
|
4
|
+
project(snapshot: DraftChangesSnapshot, event: DraftProjectionEvent): void;
|
|
5
|
+
dispose(): void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Projects the active draft into the host document and verifies the managed
|
|
9
|
+
* CSS off the commit path (0044). Verification re-checks the full surviving
|
|
10
|
+
* set: a new rule can change cascade or specificity for earlier changes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createDraftPreview(readActive: () => DraftChangesSnapshot): DraftPreview;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { getSelectedElement } from "../selection/selectionStore.js";
|
|
2
|
+
import { changeKey } from "./model.js";
|
|
3
|
+
import { verifyManagedStyleProjection } from "./managedStyleProjection.js";
|
|
4
|
+
import { isPreviewableChange } from "./types.js";
|
|
5
|
+
import { beginPreviewAttempt, clearPreviewDiagnostics, getHostPreviewDocument, publishPreviewDiagnostic, } from "./previewDiagnostics.js";
|
|
6
|
+
import { clearStructuralProjectionReports, pruneStructuralProjectionReports } from "../projection/structuralProjection.js";
|
|
7
|
+
import { applyHostDraftProjection, compileDraftProjection } from "../projection/draftProjection.js";
|
|
8
|
+
import { isEditorShellDocument } from "../runtime/editorShell.js";
|
|
9
|
+
import { isOriginalPreviewActive, subscribeOriginalPreview } from "../shell/originalPreview.js";
|
|
10
|
+
/**
|
|
11
|
+
* Projects the active draft into the host document and verifies the managed
|
|
12
|
+
* CSS off the commit path (0044). Verification re-checks the full surviving
|
|
13
|
+
* set: a new rule can change cascade or specificity for earlier changes.
|
|
14
|
+
*/
|
|
15
|
+
export function createDraftPreview(readActive) {
|
|
16
|
+
let pendingTargets = new Map();
|
|
17
|
+
let handle = null;
|
|
18
|
+
function reapply(snapshot) {
|
|
19
|
+
// While hold-to-view-original is active the host document intentionally
|
|
20
|
+
// shows the page without inspector changes. Releasing re-projects.
|
|
21
|
+
if (isOriginalPreviewActive())
|
|
22
|
+
return;
|
|
23
|
+
if (!isEditorShellDocument())
|
|
24
|
+
applyHostDraftProjection(compileDraftProjection(snapshot));
|
|
25
|
+
}
|
|
26
|
+
function flushVerification() {
|
|
27
|
+
handle = null;
|
|
28
|
+
// Probing the original preview would report the held-back changes as
|
|
29
|
+
// conflicts. Keep the pending targets; releasing re-verifies them.
|
|
30
|
+
if (isOriginalPreviewActive())
|
|
31
|
+
return;
|
|
32
|
+
const targets = pendingTargets;
|
|
33
|
+
pendingTargets = new Map();
|
|
34
|
+
// The editor shell has no application document. Renderer diagnostics are
|
|
35
|
+
// owned by renderer projection reports.
|
|
36
|
+
if (targets.size === 0 || isEditorShellDocument())
|
|
37
|
+
return;
|
|
38
|
+
const current = readActive();
|
|
39
|
+
const attempt = beginPreviewAttempt(getHostPreviewDocument(), current.revision);
|
|
40
|
+
if (!attempt)
|
|
41
|
+
return;
|
|
42
|
+
for (const change of current.changes) {
|
|
43
|
+
if (!isPreviewableChange(change))
|
|
44
|
+
continue;
|
|
45
|
+
const key = changeKey(change);
|
|
46
|
+
if (!targets.has(key))
|
|
47
|
+
continue;
|
|
48
|
+
const result = verifyManagedStyleProjection(change, targets.get(key) ?? null);
|
|
49
|
+
if (result)
|
|
50
|
+
publishPreviewDiagnostic(attempt, key, result);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function scheduleVerification() {
|
|
54
|
+
if (handle !== null)
|
|
55
|
+
return;
|
|
56
|
+
const view = typeof window !== "undefined" ? window : undefined;
|
|
57
|
+
if (view && typeof view.requestIdleCallback === "function") {
|
|
58
|
+
handle = view.requestIdleCallback(flushVerification, { timeout: 200 });
|
|
59
|
+
}
|
|
60
|
+
else if (view && typeof view.requestAnimationFrame === "function") {
|
|
61
|
+
handle = view.requestAnimationFrame(flushVerification);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
handle = setTimeout(flushVerification, 0);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function markForVerification(snapshot, verificationTargets) {
|
|
68
|
+
const selectedElement = getSelectedElement()?.domElement ?? null;
|
|
69
|
+
let added = false;
|
|
70
|
+
for (const key of snapshot.changes.map(changeKey)) {
|
|
71
|
+
if (!pendingTargets.has(key))
|
|
72
|
+
added = true;
|
|
73
|
+
// Keep the latest commit context for a merged key. The element may be in
|
|
74
|
+
// an iframe, so this is also the document for verification.
|
|
75
|
+
pendingTargets.set(key, verificationTargets?.get(key) ?? selectedElement);
|
|
76
|
+
}
|
|
77
|
+
if (added)
|
|
78
|
+
scheduleVerification();
|
|
79
|
+
}
|
|
80
|
+
const stopOriginalPreview = subscribeOriginalPreview(() => {
|
|
81
|
+
if (isEditorShellDocument())
|
|
82
|
+
return;
|
|
83
|
+
const snapshot = readActive();
|
|
84
|
+
if (isOriginalPreviewActive()) {
|
|
85
|
+
applyHostDraftProjection({
|
|
86
|
+
sourceRevision: snapshot.revision,
|
|
87
|
+
managedStyles: { rules: [], css: "" },
|
|
88
|
+
instanceOverrides: [],
|
|
89
|
+
structuralChanges: [],
|
|
90
|
+
textContentChanges: [],
|
|
91
|
+
componentOverrides: [],
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
reapply(snapshot);
|
|
96
|
+
// A flush during the hold kept its targets without scheduling, so marking
|
|
97
|
+
// alone would find every key present and schedule nothing.
|
|
98
|
+
markForVerification(snapshot);
|
|
99
|
+
if (pendingTargets.size > 0)
|
|
100
|
+
scheduleVerification();
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
project(snapshot, event) {
|
|
104
|
+
switch (event.kind) {
|
|
105
|
+
case "edit":
|
|
106
|
+
reapply(snapshot);
|
|
107
|
+
markForVerification(snapshot, event.verificationTargets);
|
|
108
|
+
return;
|
|
109
|
+
case "reconcile":
|
|
110
|
+
reapply(snapshot);
|
|
111
|
+
pruneStructuralProjectionReports(event.structuralIds);
|
|
112
|
+
markForVerification(snapshot);
|
|
113
|
+
return;
|
|
114
|
+
case "switch":
|
|
115
|
+
pendingTargets.clear();
|
|
116
|
+
reapply(snapshot);
|
|
117
|
+
markForVerification(snapshot);
|
|
118
|
+
return;
|
|
119
|
+
case "reset":
|
|
120
|
+
// A deferred verification must not inspect a new record with the
|
|
121
|
+
// selection context of the cleared one.
|
|
122
|
+
pendingTargets.clear();
|
|
123
|
+
clearPreviewDiagnostics();
|
|
124
|
+
clearStructuralProjectionReports();
|
|
125
|
+
reapply(snapshot);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
dispose() {
|
|
129
|
+
stopOriginalPreview();
|
|
130
|
+
pendingTargets.clear();
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -18,8 +18,22 @@ export interface RenderedInstanceRef {
|
|
|
18
18
|
text: string | null;
|
|
19
19
|
/** An accessible name can distinguish icon-only repeated controls. */
|
|
20
20
|
ariaLabel?: string | null;
|
|
21
|
+
/** Rendered facts used to reattach generated identities after reload. */
|
|
22
|
+
tagName?: string;
|
|
23
|
+
domPath?: string | null;
|
|
24
|
+
elementId?: string | null;
|
|
25
|
+
/**
|
|
26
|
+
* The component invocation whose entire output is this element. Present
|
|
27
|
+
* only when evidence alone cannot tell identical outputs apart.
|
|
28
|
+
*/
|
|
29
|
+
invocation?: InvocationRef;
|
|
21
30
|
};
|
|
22
31
|
}
|
|
32
|
+
/** A component callsite, as stamped by the compiler, and its component name. */
|
|
33
|
+
export interface InvocationRef {
|
|
34
|
+
callsiteId: string;
|
|
35
|
+
componentName: string;
|
|
36
|
+
}
|
|
23
37
|
export interface RenderedInstanceOverride {
|
|
24
38
|
id: string;
|
|
25
39
|
target: RenderedInstanceRef;
|
|
@@ -40,6 +54,10 @@ export interface TextProjectionTarget {
|
|
|
40
54
|
props: string | null;
|
|
41
55
|
ariaLabel: string | null;
|
|
42
56
|
beforeText: string;
|
|
57
|
+
/** Optional prompt details; never used to resolve or group text targets. */
|
|
58
|
+
tagName?: string;
|
|
59
|
+
elementId?: string | null;
|
|
60
|
+
domPath?: string | null;
|
|
43
61
|
/** Document-local evidence for a text node inside safe mixed markup. */
|
|
44
62
|
textNodePath?: readonly number[];
|
|
45
63
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
function isInvocationRef(value) {
|
|
2
|
+
return isRecord(value)
|
|
3
|
+
&& hasOnlyKeys(value, ["callsiteId", "componentName"])
|
|
4
|
+
&& typeof value.callsiteId === "string" && value.callsiteId.length > 0 && value.callsiteId.length <= 1024
|
|
5
|
+
&& typeof value.componentName === "string" && value.componentName.length > 0 && value.componentName.length <= 256;
|
|
6
|
+
}
|
|
1
7
|
export function isRenderedInstanceRef(value) {
|
|
2
8
|
if (!isRecord(value) || !hasOnlyKeys(value, ["sourceSite", "locator"]))
|
|
3
9
|
return false;
|
|
@@ -8,13 +14,17 @@ export function isRenderedInstanceRef(value) {
|
|
|
8
14
|
|| typeof source.cid !== "string"
|
|
9
15
|
|| typeof source.src !== "string"
|
|
10
16
|
|| !isRecord(locator)
|
|
11
|
-
|| !hasOnlyKeys(locator, ["kind", "occurrence", "props", "text", "ariaLabel"]))
|
|
17
|
+
|| !hasOnlyKeys(locator, ["kind", "occurrence", "props", "text", "ariaLabel", "tagName", "domPath", "elementId", "invocation"]))
|
|
12
18
|
return false;
|
|
13
19
|
return locator.kind === "evidence"
|
|
14
20
|
&& Number.isSafeInteger(locator.occurrence) && locator.occurrence >= 0
|
|
15
21
|
&& (typeof locator.props === "string" || locator.props === null)
|
|
16
22
|
&& (typeof locator.text === "string" || locator.text === null)
|
|
17
|
-
&& (locator.ariaLabel === undefined || typeof locator.ariaLabel === "string" || locator.ariaLabel === null)
|
|
23
|
+
&& (locator.ariaLabel === undefined || typeof locator.ariaLabel === "string" || locator.ariaLabel === null)
|
|
24
|
+
&& (locator.tagName === undefined || typeof locator.tagName === "string")
|
|
25
|
+
&& (locator.domPath === undefined || typeof locator.domPath === "string" || locator.domPath === null)
|
|
26
|
+
&& (locator.elementId === undefined || typeof locator.elementId === "string" || locator.elementId === null)
|
|
27
|
+
&& (locator.invocation === undefined || isInvocationRef(locator.invocation));
|
|
18
28
|
}
|
|
19
29
|
export function isRenderedInstanceOverride(value) {
|
|
20
30
|
return isRecord(value)
|
|
@@ -39,7 +49,7 @@ export function isStyleRuleContextValue(value) {
|
|
|
39
49
|
}
|
|
40
50
|
export function isTextProjectionTargetValue(value) {
|
|
41
51
|
if (!isRecord(value)
|
|
42
|
-
|| !hasOnlyKeys(value, ["sourceSite", "occurrence", "props", "ariaLabel", "beforeText", "textNodePath"])) {
|
|
52
|
+
|| !hasOnlyKeys(value, ["sourceSite", "occurrence", "props", "ariaLabel", "beforeText", "textNodePath", "tagName", "elementId", "domPath"])) {
|
|
43
53
|
return false;
|
|
44
54
|
}
|
|
45
55
|
const occurrence = value.occurrence;
|
|
@@ -51,6 +61,9 @@ export function isTextProjectionTargetValue(value) {
|
|
|
51
61
|
|| (typeof props !== "string" && props !== null)
|
|
52
62
|
|| (typeof ariaLabel !== "string" && ariaLabel !== null)
|
|
53
63
|
|| typeof beforeText !== "string"
|
|
64
|
+
|| (value.tagName !== undefined && typeof value.tagName !== "string")
|
|
65
|
+
|| (value.elementId !== undefined && value.elementId !== null && typeof value.elementId !== "string")
|
|
66
|
+
|| (value.domPath !== undefined && value.domPath !== null && typeof value.domPath !== "string")
|
|
54
67
|
|| (textNodePath !== undefined
|
|
55
68
|
&& (!Array.isArray(textNodePath)
|
|
56
69
|
|| textNodePath.length === 0
|
|
@@ -2,6 +2,7 @@ import { verifyPreview } from "../projection/managedStylesheet.js";
|
|
|
2
2
|
import { getSelectedElement } from "../selection/selectionStore.js";
|
|
3
3
|
import { instanceSelector } from "../projection/renderedInstance.js";
|
|
4
4
|
import { selectorForInteractionState } from "../shell/styleState.js";
|
|
5
|
+
import { selectorForInteractionStatePreview } from "../shell/interactionStatePreview.js";
|
|
5
6
|
import { isComponentChange, isTextContentChange, isTokenChange, } from "./types.js";
|
|
6
7
|
import { tokenReference } from "./model.js";
|
|
7
8
|
function requestedStyleValue(change) {
|
|
@@ -81,7 +82,7 @@ export function verifyManagedStyleProjection(change, selectedElement, ownerDocum
|
|
|
81
82
|
let targets = [];
|
|
82
83
|
try {
|
|
83
84
|
const selector = selectorForManagedChange(change);
|
|
84
|
-
targets = selector ? Array.from(ownerDocument.querySelectorAll(selector)) : [];
|
|
85
|
+
targets = selector ? Array.from(ownerDocument.querySelectorAll(selectorForInteractionStatePreview(selector))) : [];
|
|
85
86
|
}
|
|
86
87
|
catch {
|
|
87
88
|
targets = [];
|
|
@@ -4,7 +4,7 @@ import type { EditScope, RenderedInstanceOverride, StyleRuleContext, TextContent
|
|
|
4
4
|
/** Bounded rendered facts retained for one element without an authored source location. */
|
|
5
5
|
export interface RuntimeElementEvidence {
|
|
6
6
|
/**
|
|
7
|
-
* Why the
|
|
7
|
+
* Why the element has no authored source location.
|
|
8
8
|
* Omitted by legacy records, which are all runtime-created DOM.
|
|
9
9
|
*/
|
|
10
10
|
reason?: "runtime-created" | "unannotated";
|
|
@@ -12,6 +12,19 @@ export interface RuntimeElementEvidence {
|
|
|
12
12
|
text: string | null;
|
|
13
13
|
props: string | null;
|
|
14
14
|
ariaLabel: string | null;
|
|
15
|
+
/** Nearest-ancestor selector path; omitted by records captured before it existed. */
|
|
16
|
+
domPath?: string | null;
|
|
17
|
+
elementId?: string | null;
|
|
18
|
+
}
|
|
19
|
+
/** Bounded authored facts for one annotated element. */
|
|
20
|
+
export interface SourceElementEvidence {
|
|
21
|
+
tagName: string;
|
|
22
|
+
props: string | null;
|
|
23
|
+
/** Rendered identification details; omitted by older saved changes. */
|
|
24
|
+
text?: string | null;
|
|
25
|
+
ariaLabel?: string | null;
|
|
26
|
+
domPath?: string | null;
|
|
27
|
+
elementId?: string | null;
|
|
15
28
|
}
|
|
16
29
|
export interface ElementChangeRecord {
|
|
17
30
|
kind?: "element";
|
|
@@ -37,7 +50,11 @@ export interface ElementChangeRecord {
|
|
|
37
50
|
};
|
|
38
51
|
/** Present when the selected element has a document-local runtime identity. */
|
|
39
52
|
runtimeEvidence?: RuntimeElementEvidence;
|
|
53
|
+
/** Authored facts that relocate the source site after its position shifts. */
|
|
54
|
+
sourceEvidence?: SourceElementEvidence;
|
|
40
55
|
scope?: EditScope;
|
|
56
|
+
/** Matching rendered items when captured; presentation only, absent in older edits. */
|
|
57
|
+
sourceInstanceCount?: number;
|
|
41
58
|
/** Durable, controller-owned target for one rendered output. */
|
|
42
59
|
instanceOverride?: RenderedInstanceOverride;
|
|
43
60
|
state?: "base" | "hover" | "active" | "focus" | "focus-visible" | "disabled";
|
|
@@ -4,7 +4,9 @@ import { IconCheck, IconTrash } from "@tabler/icons-react";
|
|
|
4
4
|
import { useCanvasMode } from "../canvas/viewStore.js";
|
|
5
5
|
import { getRegisteredFrames } from "../canvas/projection.js";
|
|
6
6
|
import { subscribeCanvasRendererMessages } from "../canvas/rendererMessageRouter.js";
|
|
7
|
-
import {
|
|
7
|
+
import { measureFrameViewport, toViewportPoint } from "../canvas/frameConnection.js";
|
|
8
|
+
import { canvasFrameElement } from "../canvas/canvasFrame.js";
|
|
9
|
+
import { getElementComputedStyle } from "../runtime/domRealm.js";
|
|
8
10
|
import { captureRenderedInstance } from "../projection/renderedInstance.js";
|
|
9
11
|
import { IconButton } from "../ui/IconButton.js";
|
|
10
12
|
import { TextInput } from "../ui/TextInput.js";
|
|
@@ -12,21 +14,21 @@ import { commentFingerprint, commentViewport, resolveCommentElement } from "./el
|
|
|
12
14
|
import { commentRoute, getCommentClearRevision, isCommentToolActive, removeComment, saveComment, subscribeComments, useComments, useCommentToolActive } from "./store.js";
|
|
13
15
|
import { isReadingCommentSource, readCommentSource, reconcileComments } from "./verification.js";
|
|
14
16
|
const styles = ".comment-pin {\n position: fixed;\n z-index: 10;\n display: grid;\n place-items: center;\n width: var(--icon-size-large);\n height: var(--icon-size-large);\n padding: 0;\n border: 2px solid var(--surface-raised-2x);\n border-radius: 50%;\n background: var(--accent);\n box-shadow: var(--shadow-popup);\n cursor: pointer;\n pointer-events: auto;\n}\n\n.comment-editor-backdrop {\n position: fixed;\n inset: 0;\n z-index: 11;\n pointer-events: auto;\n}\n\n.comment-editor {\n position: fixed;\n z-index: 12;\n display: flex;\n align-items: center;\n gap: var(--space-4);\n width: min(384px, calc(100vw - 16px));\n box-sizing: border-box;\n padding: var(--space-8);\n border-radius: var(--radius-12);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n pointer-events: auto;\n}\n\n.comment-editor .text-input { flex: 1; min-width: 0; padding-inline: var(--space-8); }\n.comment-editor__error { position: absolute; top: 100%; left: 0; padding: var(--space-8); background: var(--surface-raised-2x); color: var(--text-primary); border-radius: var(--radius-control); }\n";
|
|
17
|
+
import { InspectorStyle } from "../ui/InspectorStyle.js";
|
|
15
18
|
function sameAnchor(left, right) {
|
|
16
19
|
return left?.left === right?.left && left?.top === right?.top;
|
|
17
20
|
}
|
|
18
21
|
function anchorFor(iframe, element) {
|
|
19
22
|
if (!element.isConnected || iframe.contentDocument !== element.ownerDocument)
|
|
20
23
|
return null;
|
|
21
|
-
// Focus view keeps
|
|
22
|
-
|
|
24
|
+
// Focus view keeps real frames mounted, but their pins must stay hidden.
|
|
25
|
+
const frameElement = canvasFrameElement(iframe);
|
|
26
|
+
if (frameElement && getElementComputedStyle(frameElement).visibility !== "visible")
|
|
23
27
|
return null;
|
|
24
|
-
const frame = iframe
|
|
28
|
+
const frame = measureFrameViewport(iframe);
|
|
25
29
|
const rect = element.getBoundingClientRect();
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const top = frame.top + rect.top * scale;
|
|
29
|
-
if (top < frame.top || top > frame.bottom || left < frame.left || left > frame.right)
|
|
30
|
+
const { x: left, y: top } = toViewportPoint(frame, { x: rect.right, y: rect.top });
|
|
31
|
+
if (top < frame.top || top > frame.top + frame.height || left < frame.left || left > frame.left + frame.width)
|
|
30
32
|
return null;
|
|
31
33
|
return { left, top };
|
|
32
34
|
}
|
|
@@ -75,17 +77,17 @@ export function CommentsOverlay() {
|
|
|
75
77
|
window.addEventListener("keydown", dismissOnEscape, true);
|
|
76
78
|
return () => window.removeEventListener("keydown", dismissOnEscape, true);
|
|
77
79
|
}, [draft]);
|
|
78
|
-
useEffect(() => subscribeCanvasRendererMessages(({
|
|
80
|
+
useEffect(() => subscribeCanvasRendererMessages(({ frame, message }) => {
|
|
79
81
|
if (message.type === "element-click" && isCommentToolActive()) {
|
|
80
|
-
const element =
|
|
81
|
-
if (element
|
|
82
|
-
openDraft({ element, iframe });
|
|
83
|
-
}
|
|
82
|
+
const element = frame.element(message);
|
|
83
|
+
if (element)
|
|
84
|
+
openDraft({ element, iframe: frame.iframe });
|
|
84
85
|
}
|
|
85
86
|
if (message.type === "element-deselect")
|
|
86
87
|
setDraft(null);
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
const frameDocument = frame.document();
|
|
89
|
+
if (message.type === "frame-ready" && frameDocument) {
|
|
90
|
+
void reconcileComments(frameDocument).catch(() => undefined);
|
|
89
91
|
}
|
|
90
92
|
}), []);
|
|
91
93
|
useEffect(() => {
|
|
@@ -239,7 +241,7 @@ export function CommentsOverlay() {
|
|
|
239
241
|
}
|
|
240
242
|
if (mode !== "canvas")
|
|
241
243
|
return null;
|
|
242
|
-
return _jsxs(_Fragment, { children: [_jsx(
|
|
244
|
+
return _jsxs(_Fragment, { children: [_jsx(InspectorStyle, { css: styles }), comments.flatMap((comment) => [...anchors].filter(([key]) => key.startsWith(`${comment.id}:`)).map(([key, anchor]) => (_jsx("button", { type: "button", className: "comment-pin", "data-test": "comment-pin", "aria-label": `Comment: ${comment.note}`, title: comment.note, style: anchor, onClick: () => {
|
|
243
245
|
const bound = bindings.current.get(key);
|
|
244
246
|
if (bound)
|
|
245
247
|
openDraft({ ...bound, existing: comment });
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { resolveRenderedInstance } from "../projection/renderedInstance.js";
|
|
2
|
-
import { sourceSiteSelector } from "../selection/sourceSite.js";
|
|
2
|
+
import { relocateSourceSite, sourceSiteSelector } from "../selection/sourceSite.js";
|
|
3
3
|
/** Retains a changed target only when its source site still identifies one output. */
|
|
4
4
|
export function resolveCommentElement(doc, target) {
|
|
5
5
|
const exact = resolveRenderedInstance(doc, target);
|
|
6
6
|
if (exact.status === "resolved")
|
|
7
7
|
return exact.element;
|
|
8
|
-
const
|
|
8
|
+
const { props, ariaLabel } = target.locator;
|
|
9
|
+
const sourceSite = relocateSourceSite(doc, target.sourceSite, (element) => element.getAttribute("data-cprops") === props && element.getAttribute("aria-label") === (ariaLabel ?? null));
|
|
10
|
+
if (sourceSite) {
|
|
11
|
+
const moved = resolveRenderedInstance(doc, { ...target, sourceSite });
|
|
12
|
+
if (moved.status === "resolved")
|
|
13
|
+
return moved.element;
|
|
14
|
+
}
|
|
15
|
+
const site = sourceSite ?? target.sourceSite;
|
|
16
|
+
const selector = sourceSiteSelector(site.cid, site.src);
|
|
9
17
|
if (!selector)
|
|
10
18
|
return null;
|
|
11
19
|
const candidates = Array.from(doc.querySelectorAll(selector));
|
|
12
20
|
if (candidates.length === 1)
|
|
13
21
|
return candidates[0];
|
|
14
|
-
const { props, ariaLabel } = target.locator;
|
|
15
22
|
if (!props && !ariaLabel)
|
|
16
23
|
return null;
|
|
17
24
|
const matching = candidates.filter((element) => element.getAttribute("data-cprops") === props
|