nudge-ui 0.2.1 → 0.2.3
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/README.md +164 -46
- package/dist/client.mjs +252 -54
- package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
- package/dist/css/value-semantics/colorSemantics.js +17 -0
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
- package/dist/hosts/astro/clientTransport.js +41 -32
- package/dist/hosts/astro/integration.d.ts +2 -2
- package/dist/hosts/astro/integration.js +9 -4
- package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
- package/dist/hosts/astro/markdownIdentity.js +81 -0
- package/dist/hosts/environment.d.ts +15 -0
- package/dist/hosts/environment.js +16 -0
- package/dist/hosts/next/manifest.js +1 -1
- package/dist/hosts/next/sidecar.js +12 -2
- package/dist/hosts/next/wrapper.d.ts +2 -0
- package/dist/hosts/next/wrapper.js +2 -1
- package/dist/hosts/projectBridge.d.ts +1 -0
- package/dist/hosts/projectBridge.js +1 -0
- package/dist/hosts/static/manifest.js +3 -1
- package/dist/hosts/static/server.js +4 -0
- package/dist/hosts/vite/vite.d.ts +4 -0
- package/dist/hosts/vite/vite.js +28 -5
- package/dist/inspector/agent/McpConnectionDialog.d.ts +3 -2
- package/dist/inspector/agent/McpConnectionDialog.js +23 -22
- package/dist/inspector/agent/activity.d.ts +18 -0
- package/dist/inspector/agent/activity.js +70 -0
- package/dist/inspector/agent/client.d.ts +8 -0
- package/dist/inspector/agent/client.js +65 -5
- package/dist/inspector/agent/connectionStatus.d.ts +3 -3
- package/dist/inspector/agent/connectionStatus.js +9 -2
- package/dist/inspector/agent/httpTransport.d.ts +6 -0
- package/dist/inspector/agent/httpTransport.js +50 -3
- package/dist/inspector/agent/protocol.d.ts +9 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +11 -1
- package/dist/inspector/agent/verification.js +53 -25
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
- package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
- package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
- package/dist/inspector/canvas/CanvasCard.js +133 -38
- package/dist/inspector/canvas/CanvasElementOverlay.js +200 -22
- package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
- package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
- package/dist/inspector/canvas/CanvasToolbar.js +8 -4
- package/dist/inspector/canvas/CanvasWorkspace.js +243 -56
- package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
- package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
- package/dist/inspector/canvas/agentPresentation.js +64 -52
- package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
- package/dist/inspector/canvas/canvasGestures.js +1 -1
- package/dist/inspector/canvas/canvasStore.d.ts +46 -82
- package/dist/inspector/canvas/canvasStore.js +251 -284
- package/dist/inspector/canvas/frameContent.d.ts +21 -0
- package/dist/inspector/canvas/frameContent.js +44 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +34 -4
- package/dist/inspector/canvas/frameProtocol.js +9 -2
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -1
- package/dist/inspector/canvas/keyboardShortcuts.js +5 -3
- package/dist/inspector/canvas/layout.d.ts +52 -0
- package/dist/inspector/canvas/layout.js +106 -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 +15 -5
- package/dist/inspector/canvas/projection.js +69 -13
- package/dist/inspector/canvas/rendererBootstrap.js +45 -4
- package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
- package/dist/inspector/canvas/rendererElementSelector.js +102 -45
- package/dist/inspector/canvas/rendererStylesheet.js +2 -2
- package/dist/inspector/canvas/resizeHandleScale.js +1 -1
- package/dist/inspector/canvas/sessionStore.d.ts +11 -26
- package/dist/inspector/canvas/sessionStore.js +43 -183
- package/dist/inspector/canvas/staleChangeDetector.js +9 -3
- package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
- package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
- package/dist/inspector/canvas/viewStore.d.ts +38 -0
- package/dist/inspector/canvas/viewStore.js +104 -0
- package/dist/inspector/canvas/wheelPan.d.ts +8 -0
- package/dist/inspector/canvas/wheelPan.js +8 -0
- package/dist/inspector/changes/changesLog.d.ts +6 -5
- package/dist/inspector/changes/changesLog.js +40 -32
- package/dist/inspector/changes/draftChanges.d.ts +56 -0
- package/dist/inspector/changes/draftChanges.js +300 -0
- package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
- package/dist/inspector/changes/previewDiagnostics.js +11 -25
- package/dist/inspector/clientManifest.d.ts +3 -1
- package/dist/inspector/clientManifest.js +1 -0
- package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
- package/dist/inspector/comments/CommentsOverlay.js +250 -0
- package/dist/inspector/comments/element.d.ts +6 -0
- package/dist/inspector/comments/element.js +47 -0
- package/dist/inspector/comments/store.d.ts +29 -0
- package/dist/inspector/comments/store.js +121 -0
- package/dist/inspector/comments/verification.d.ts +5 -0
- package/dist/inspector/comments/verification.js +52 -0
- package/dist/inspector/drafts/model.d.ts +35 -0
- package/dist/inspector/drafts/model.js +45 -0
- package/dist/inspector/drafts/store.d.ts +28 -0
- package/dist/inspector/drafts/store.js +231 -0
- package/dist/inspector/index.js +26 -35
- package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
- package/dist/inspector/iterations/capture.d.ts +2 -0
- package/dist/inspector/iterations/capture.js +173 -0
- package/dist/inspector/iterations/client.d.ts +7 -0
- package/dist/inspector/iterations/client.js +52 -0
- package/dist/inspector/iterations/coordinator.d.ts +15 -0
- package/dist/inspector/iterations/coordinator.js +130 -0
- package/dist/inspector/iterations/prompt.d.ts +3 -0
- package/dist/inspector/iterations/prompt.js +26 -0
- package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
- package/dist/inspector/overlay/DomNavigator.js +46 -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/interactionStyles.js +3 -1
- package/dist/inspector/overlay/spacingGestures.d.ts +15 -3
- package/dist/inspector/overlay/spacingGestures.js +88 -13
- package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
- package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
- package/dist/inspector/projection/renderedInstance.d.ts +2 -1
- package/dist/inspector/projection/renderedInstance.js +9 -5
- package/dist/inspector/projection/structuralProjection.d.ts +2 -1
- package/dist/inspector/projection/structuralProjection.js +19 -15
- package/dist/inspector/projection/textProjection.d.ts +2 -1
- package/dist/inspector/projection/textProjection.js +8 -4
- package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
- package/dist/inspector/prompt/clipboardHandoff.d.ts +10 -18
- package/dist/inspector/prompt/clipboardHandoff.js +92 -143
- package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
- package/dist/inspector/prompt/generatePrompt.js +17 -16
- package/dist/inspector/runtime/runtimeConfig.d.ts +10 -1
- package/dist/inspector/runtime/runtimeConfig.js +24 -0
- package/dist/inspector/selection/hierarchy.d.ts +6 -0
- package/dist/inspector/selection/hierarchy.js +8 -0
- package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
- package/dist/inspector/settings/SettingsDialog.js +22 -9
- package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
- package/dist/inspector/shell/AgentStatusButton.js +36 -0
- package/dist/inspector/shell/AppShell.js +4 -2
- package/dist/inspector/shell/ChangesLog.js +47 -27
- package/dist/inspector/shell/CopyPromptButton.js +146 -90
- package/dist/inspector/shell/EmptyState.js +9 -8
- package/dist/inspector/shell/InspectorShell.js +14 -25
- package/dist/inspector/shell/PeekOriginalButton.js +24 -23
- package/dist/inspector/shell/originalPreview.js +5 -5
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +11 -0
- package/dist/inspector/sketch/SketchEditor.js +14 -14
- package/dist/inspector/sketch/SketchEntryButton.js +1 -1
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
- package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
- package/dist/inspector/sketch/SketchOverlay.js +7 -7
- package/dist/inspector/sketch/SketchWorkspace.js +18 -12
- package/dist/inspector/sketch/interaction.d.ts +1 -0
- package/dist/inspector/sketch/interaction.js +8 -4
- package/dist/inspector/sketch/model.d.ts +5 -0
- package/dist/inspector/sketch/model.js +6 -2
- package/dist/inspector/sketch/raster.d.ts +1 -2
- package/dist/inspector/sketch/raster.js +68 -4
- package/dist/inspector/sketch/store.js +48 -23
- package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
- package/dist/inspector/styleEditors/BorderEditor.js +49 -78
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
- package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
- package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
- package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
- package/dist/inspector/styleEditors/ColorPicker.js +10 -4
- package/dist/inspector/styleEditors/GapField.js +3 -5
- package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
- package/dist/inspector/styleEditors/GridChildSection.js +18 -9
- package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
- package/dist/inspector/styleEditors/GridPicker.js +8 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/GridValueField.js +19 -14
- package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
- package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
- package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
- package/dist/inspector/styleEditors/LayoutSection.js +32 -11
- package/dist/inspector/styleEditors/SpacingBox.js +16 -9
- package/dist/inspector/styleEditors/Typography.js +11 -3
- package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
- package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
- package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
- package/dist/inspector/styleEditors/gradientValue.js +114 -0
- package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
- package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
- package/dist/inspector/styleEditors/layoutValue.js +0 -42
- package/dist/inspector/styleEditors/nudgeValue.d.ts +4 -4
- package/dist/inspector/styleEditors/nudgeValue.js +41 -11
- package/dist/inspector/tokens/TokenField.d.ts +7 -0
- package/dist/inspector/tokens/TokenField.js +49 -22
- package/dist/inspector/tokens/TokenLabel.js +5 -14
- 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 +1 -1
- package/dist/inspector/tokens/editActions.js +1 -1
- package/dist/inspector/ui/AtRuleContext.js +4 -6
- package/dist/inspector/ui/Button.d.ts +6 -2
- package/dist/inspector/ui/Button.js +4 -3
- package/dist/inspector/ui/ControlSurface.d.ts +2 -2
- package/dist/inspector/ui/ControlSurface.js +4 -3
- package/dist/inspector/ui/Disclosure.d.ts +22 -0
- package/dist/inspector/ui/Disclosure.js +13 -0
- package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
- package/dist/inspector/ui/InlineStyleWarning.js +13 -0
- package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
- package/dist/inspector/ui/InspectorPopover.js +4 -5
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +8 -7
- package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
- package/dist/inspector/ui/SegmentedControl.js +2 -2
- package/dist/inspector/ui/Select.d.ts +6 -1
- package/dist/inspector/ui/Select.js +7 -11
- package/dist/inspector/ui/SideValuesField.js +1 -1
- package/dist/inspector/ui/Tooltip.d.ts +26 -0
- package/dist/inspector/ui/Tooltip.js +32 -0
- package/dist/inspector/ui/classNames.d.ts +2 -0
- package/dist/inspector/ui/classNames.js +4 -0
- package/dist/inspector/ui/customCursors.d.ts +23 -0
- package/dist/inspector/ui/customCursors.js +122 -0
- package/dist/inspector/ui/dragCursor.d.ts +6 -0
- package/dist/inspector/ui/dragCursor.js +13 -0
- package/dist/inspector/ui/portalContainer.d.ts +2 -0
- package/dist/inspector/ui/portalContainer.js +6 -0
- package/dist/inspector/ui/styles.js +34 -24
- package/dist/inspector/workspace/commands.d.ts +9 -0
- package/dist/inspector/workspace/commands.js +82 -0
- package/dist/inspector/workspace/controller.d.ts +4 -0
- package/dist/inspector/workspace/controller.js +66 -0
- package/dist/inspector/workspace/handoff.d.ts +42 -0
- package/dist/inspector/workspace/handoff.js +115 -0
- package/dist/inspector/workspace/timeline.d.ts +30 -0
- package/dist/inspector/workspace/timeline.js +108 -0
- package/dist/internal/agent-protocol.d.ts +19 -2
- package/dist/internal/agent-protocol.js +13 -0
- package/dist/nudge-ui.mjs +560 -299
- package/dist/project/artifacts.d.ts +13 -0
- package/dist/project/artifacts.js +253 -0
- package/dist/transport/artifacts.d.ts +8 -0
- package/dist/transport/artifacts.js +9 -0
- package/dist/transport/editor.d.ts +20 -1
- package/dist/transport/editor.js +44 -11
- package/dist/transport/routes.d.ts +6 -1
- package/dist/transport/routes.js +6 -1
- package/package.json +6 -2
- package/dist/inspector/changes/workspaceChanges.d.ts +0 -43
- package/dist/inspector/changes/workspaceChanges.js +0 -239
- package/dist/inspector/shell/DomNavigation.d.ts +0 -8
- package/dist/inspector/shell/DomNavigation.js +0 -30
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type DraftTarget } from "../drafts/model.js";
|
|
2
|
+
/** Content identity is independent of frame geometry and its editable draft. */
|
|
3
|
+
export type FrameContent = {
|
|
4
|
+
kind: "route";
|
|
5
|
+
url: string;
|
|
6
|
+
navigationUrl?: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "iteration";
|
|
9
|
+
artifactId: string;
|
|
10
|
+
sourceUrl: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function contentSourceUrl(content: FrameContent | undefined): string;
|
|
13
|
+
export declare function iterationId(content: FrameContent | undefined): string | undefined;
|
|
14
|
+
export declare function contentNavigationUrl(content: FrameContent): string | undefined;
|
|
15
|
+
export declare function contentDocumentUrl(content: FrameContent): string;
|
|
16
|
+
export declare function contentEditTarget(content: FrameContent): DraftTarget;
|
|
17
|
+
export declare function getLinkedFrameIds(frames: readonly {
|
|
18
|
+
id: string;
|
|
19
|
+
content: FrameContent;
|
|
20
|
+
}[], activeId: string | null): readonly string[];
|
|
21
|
+
export declare function isFrameContent(value: unknown, origin: string): value is FrameContent;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { applicationTarget, htmlTarget } from "../drafts/model.js";
|
|
2
|
+
import { artifactDocumentUrl } from "../iterations/client.js";
|
|
3
|
+
export function contentSourceUrl(content) {
|
|
4
|
+
return content ? content.kind === "route" ? content.url : content.sourceUrl : "";
|
|
5
|
+
}
|
|
6
|
+
export function iterationId(content) {
|
|
7
|
+
return content?.kind === "iteration" ? content.artifactId : undefined;
|
|
8
|
+
}
|
|
9
|
+
export function contentNavigationUrl(content) {
|
|
10
|
+
return content.kind === "route" ? content.navigationUrl : undefined;
|
|
11
|
+
}
|
|
12
|
+
export function contentDocumentUrl(content) {
|
|
13
|
+
return content.kind === "route" ? content.navigationUrl ?? content.url : artifactDocumentUrl(content.artifactId);
|
|
14
|
+
}
|
|
15
|
+
export function contentEditTarget(content) {
|
|
16
|
+
return content.kind === "route" ? applicationTarget(content.url) : htmlTarget(content.artifactId);
|
|
17
|
+
}
|
|
18
|
+
export function getLinkedFrameIds(frames, activeId) {
|
|
19
|
+
const active = frames.find((frame) => frame.id === activeId);
|
|
20
|
+
if (active?.content.kind !== "route")
|
|
21
|
+
return [];
|
|
22
|
+
const route = applicationTarget(active.content.url).route;
|
|
23
|
+
const matching = frames.filter((frame) => frame.content.kind === "route"
|
|
24
|
+
&& applicationTarget(frame.content.url).route === route);
|
|
25
|
+
return matching.length > 1 ? matching.map((frame) => frame.id) : [];
|
|
26
|
+
}
|
|
27
|
+
export function isFrameContent(value, origin) {
|
|
28
|
+
if (!value || typeof value !== "object")
|
|
29
|
+
return false;
|
|
30
|
+
const content = value;
|
|
31
|
+
const url = content.kind === "route" ? content.url : content.sourceUrl;
|
|
32
|
+
if (typeof url !== "string")
|
|
33
|
+
return false;
|
|
34
|
+
try {
|
|
35
|
+
if (new URL(url).origin !== origin)
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (content.kind === "iteration")
|
|
42
|
+
return typeof content.artifactId === "string" && /^[a-f0-9-]{36}$/i.test(content.artifactId);
|
|
43
|
+
return content.kind === "route" && content.navigationUrl === undefined;
|
|
44
|
+
}
|
|
@@ -2,7 +2,7 @@ import type { ComponentOverride } from "../componentSemantics/types.js";
|
|
|
2
2
|
import type { RenderedInstanceOverride } from "../changes/editModel.js";
|
|
3
3
|
import type { NudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
|
|
4
4
|
import type { SpacingDescriptor } from "../overlay/spacingGestures.js";
|
|
5
|
-
export declare const PROTOCOL_VERSION =
|
|
5
|
+
export declare const PROTOCOL_VERSION = 21;
|
|
6
6
|
export interface FrameMessage {
|
|
7
7
|
type: string;
|
|
8
8
|
protocolVersion: number;
|
|
@@ -93,6 +93,13 @@ export interface TextProjectionReportMessage extends RendererMessage {
|
|
|
93
93
|
export interface NavigationIntentMessage extends RendererMessage {
|
|
94
94
|
type: "navigation-intent";
|
|
95
95
|
url: string;
|
|
96
|
+
/** The renderer kept its own route and asks the controller for a card instead. */
|
|
97
|
+
openInCard?: boolean;
|
|
98
|
+
}
|
|
99
|
+
/** Tells a renderer whether ordinary link clicks open a new canvas card. */
|
|
100
|
+
export interface LinkTargetStateMessage extends RendererMessage {
|
|
101
|
+
type: "link-target-state";
|
|
102
|
+
openInCard: boolean;
|
|
96
103
|
}
|
|
97
104
|
export interface ElementHoverMessage extends RendererMessage {
|
|
98
105
|
type: "element-hover";
|
|
@@ -123,8 +130,12 @@ export interface ElementHoverMessage extends RendererMessage {
|
|
|
123
130
|
x: number;
|
|
124
131
|
y: number;
|
|
125
132
|
} | null;
|
|
126
|
-
/** The
|
|
133
|
+
/** The spacing handle under the pointer. Drives the resize cursor and drag
|
|
134
|
+
* start; only present when the pointer is inside the handle hit area. */
|
|
127
135
|
spacing?: SpacingDescriptor | null;
|
|
136
|
+
/** The padding bar or layout gap area under the pointer, regardless of the
|
|
137
|
+
* handle. Drives the hover fill overlay across the whole area. */
|
|
138
|
+
hoverSpacing?: SpacingDescriptor | null;
|
|
128
139
|
}
|
|
129
140
|
/** Modifier state stays inside one renderer frame; the parent never infers it
|
|
130
141
|
* from its own keyboard events because focus lives in the iframe. */
|
|
@@ -179,6 +190,8 @@ export interface ElementDragStartMessage extends RendererMessage {
|
|
|
179
190
|
};
|
|
180
191
|
/** Spacing affordance captured at pointer-down, if this is a spacing drag. */
|
|
181
192
|
spacing?: SpacingDescriptor | null;
|
|
193
|
+
/** True while Shift is held; spacing drags use this to snap by 8px steps. */
|
|
194
|
+
shiftKey?: boolean;
|
|
182
195
|
}
|
|
183
196
|
export interface ElementDragMoveMessage extends RendererMessage {
|
|
184
197
|
type: "element-drag-move";
|
|
@@ -186,6 +199,8 @@ export interface ElementDragMoveMessage extends RendererMessage {
|
|
|
186
199
|
x: number;
|
|
187
200
|
y: number;
|
|
188
201
|
};
|
|
202
|
+
/** True while Shift is held; spacing drags use this to snap by 8px steps. */
|
|
203
|
+
shiftKey?: boolean;
|
|
189
204
|
}
|
|
190
205
|
export interface ElementDragEndMessage extends RendererMessage {
|
|
191
206
|
type: "element-drag-end";
|
|
@@ -193,6 +208,8 @@ export interface ElementDragEndMessage extends RendererMessage {
|
|
|
193
208
|
x: number;
|
|
194
209
|
y: number;
|
|
195
210
|
};
|
|
211
|
+
/** True while Shift is held; spacing drags use this to snap by 8px steps. */
|
|
212
|
+
shiftKey?: boolean;
|
|
196
213
|
/** True when the renderer lost the gesture before the pointer was released. */
|
|
197
214
|
cancelled?: boolean;
|
|
198
215
|
}
|
|
@@ -222,7 +239,7 @@ export interface InspectorToggleRequestMessage extends RendererMessage {
|
|
|
222
239
|
export interface InspectorOpenRequestMessage extends RendererMessage {
|
|
223
240
|
type: "inspector-open-request";
|
|
224
241
|
}
|
|
225
|
-
export type KeyboardShortcutCode = "Backslash" | "KeyV" | "
|
|
242
|
+
export type KeyboardShortcutCode = "Backslash" | "KeyI" | "KeyV" | "KeyC" | "KeyP" | "KeyS";
|
|
226
243
|
export type KeyboardShortcutPhase = "keydown" | "keyup";
|
|
227
244
|
/** Forwards controller-owned keyboard shortcuts from an iframe document. */
|
|
228
245
|
export interface KeyboardShortcutMessage extends RendererMessage {
|
|
@@ -261,6 +278,8 @@ export interface MeasureModifierMessage extends RendererMessage {
|
|
|
261
278
|
export interface BoardGestureStateMessage extends RendererMessage {
|
|
262
279
|
type: "board-gesture-state";
|
|
263
280
|
enabled: boolean;
|
|
281
|
+
/** Ordinary wheel gestures pan the board instead of scrolling the app. */
|
|
282
|
+
panScroll?: boolean;
|
|
264
283
|
}
|
|
265
284
|
/** Proxies modified wheel gestures that originate inside an iframe card. */
|
|
266
285
|
export interface ZoomMessage extends RendererMessage {
|
|
@@ -271,7 +290,18 @@ export interface ZoomMessage extends RendererMessage {
|
|
|
271
290
|
y: number;
|
|
272
291
|
};
|
|
273
292
|
}
|
|
274
|
-
|
|
293
|
+
/** Proxies ordinary wheel gestures as normalized pixel distances. */
|
|
294
|
+
export interface WheelPanMessage extends RendererMessage {
|
|
295
|
+
type: "wheel-pan";
|
|
296
|
+
deltaX: number;
|
|
297
|
+
deltaY: number;
|
|
298
|
+
}
|
|
299
|
+
/** Keeps the A key state synchronized when keyboard focus moves into an iframe. */
|
|
300
|
+
export interface AppInteractionModifierMessage extends RendererMessage {
|
|
301
|
+
type: "app-interaction-modifier";
|
|
302
|
+
held: boolean;
|
|
303
|
+
}
|
|
304
|
+
export type FrameProtocolMessage = ParentReadyMessage | RendererHelloMessage | FrameReadyMessage | FrameRuntimeMessage | FrameMetadataMessage | FrameLoadError | ReplaceStylesMessage | ProjectionAppliedMessage | StructuralProjectionReportMessage | RenderedInstanceProjectionReportMessage | TextProjectionReportMessage | NavigationIntentMessage | LinkTargetStateMessage | ElementHoverMessage | ElementMeasureStateMessage | ElementClickMessage | InlineTextIntentMessage | ElementDeselectMessage | ElementDragStartMessage | ElementDragMoveMessage | ElementDragEndMessage | ElementDeleteMessage | ElementNudgeMessage | HistoryRequestMessage | InspectorToggleRequestMessage | InspectorOpenRequestMessage | KeyboardShortcutMessage | PanStartMessage | PanMoveMessage | PanEndMessage | PanModifierMessage | MeasureModifierMessage | BoardGestureStateMessage | ZoomMessage | WheelPanMessage | AppInteractionModifierMessage;
|
|
275
305
|
export declare function setRendererIdentity(identity: FrameIdentity): void;
|
|
276
306
|
export declare function clearRendererIdentity(expected?: FrameIdentity): void;
|
|
277
307
|
export declare function getRendererIdentity(): FrameIdentity | null;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { isDocumentProjectionReport } from "../projection/renderedInstance.js";
|
|
2
2
|
import { isTextProjectionReport } from "../projection/textProjection.js";
|
|
3
3
|
import { isStructuralProjectionReport } from "../projection/structuralProjectionBoundary.js";
|
|
4
|
+
// v21 adds the Comment tool cursor and C shortcut.
|
|
5
|
+
// v20 adds Shift state to element drag messages so spacing handles can snap
|
|
6
|
+
// changes to 8px increments.
|
|
4
7
|
// v19 adds spacing drag gestures (hover point, spacing descriptor, start
|
|
5
8
|
// point, and cancelled drag-end) plus renderer-to-controller keyboard shortcut
|
|
6
9
|
// forwarding for Canvas frames.
|
|
@@ -15,7 +18,7 @@ import { isStructuralProjectionReport } from "../projection/structuralProjection
|
|
|
15
18
|
// rereading a Canvas iframe until the renderer has applied its revision. v11
|
|
16
19
|
// added the renderer-hello handshake solicitation for runtimes whose boot
|
|
17
20
|
// completes after the controller's load-time parent-ready.
|
|
18
|
-
export const PROTOCOL_VERSION =
|
|
21
|
+
export const PROTOCOL_VERSION = 21;
|
|
19
22
|
let rendererIdentity = null;
|
|
20
23
|
export function setRendererIdentity(identity) {
|
|
21
24
|
rendererIdentity = identity;
|
|
@@ -37,7 +40,11 @@ export function isRendererMessageFor(value, identity) {
|
|
|
37
40
|
&& message.cardId === identity.cardId;
|
|
38
41
|
}
|
|
39
42
|
export function isKeyboardShortcutCode(value) {
|
|
40
|
-
return value === "Backslash"
|
|
43
|
+
return value === "Backslash"
|
|
44
|
+
|| value === "KeyV"
|
|
45
|
+
|| value === "KeyC"
|
|
46
|
+
|| value === "KeyP"
|
|
47
|
+
|| value === "KeyS";
|
|
41
48
|
}
|
|
42
49
|
/** Strict JSON-only schema for renderer-originated global keyboard shortcuts. */
|
|
43
50
|
export function isKeyboardShortcutMessage(value, identity) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/** `select` leaves the application interactive; `design` turns clicks into inspector selection. */
|
|
2
|
+
export type CanvasInteractionTool = "select" | "design" | "comment" | "sketch";
|
|
3
|
+
/** Physical key used to temporarily interact with the app in the canvas. */
|
|
4
|
+
export declare const APP_INTERACTION_KEY = "KeyA";
|
|
2
5
|
export declare function canvasToolForCode(code: string): CanvasInteractionTool | null;
|
|
3
6
|
export declare function canvasToolForEvent(event: KeyboardEvent): CanvasInteractionTool | null;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
/** Physical key used to temporarily interact with the app in the canvas. */
|
|
2
|
+
export const APP_INTERACTION_KEY = "KeyA";
|
|
1
3
|
export function canvasToolForCode(code) {
|
|
2
4
|
if (code === "KeyV")
|
|
3
|
-
return "
|
|
4
|
-
if (code === "
|
|
5
|
-
return "
|
|
5
|
+
return "design";
|
|
6
|
+
if (code === "KeyC")
|
|
7
|
+
return "comment";
|
|
6
8
|
if (code === "KeyP")
|
|
7
9
|
return "sketch";
|
|
8
10
|
return null;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface LayoutCard {
|
|
2
|
+
readonly id: string;
|
|
3
|
+
readonly x: number;
|
|
4
|
+
readonly y: number;
|
|
5
|
+
readonly width: number;
|
|
6
|
+
readonly height: number;
|
|
7
|
+
}
|
|
8
|
+
export interface Bounds {
|
|
9
|
+
readonly minX: number;
|
|
10
|
+
readonly minY: number;
|
|
11
|
+
readonly maxX: number;
|
|
12
|
+
readonly maxY: number;
|
|
13
|
+
}
|
|
14
|
+
/** The layout fields a creation may change on existing frames. */
|
|
15
|
+
export interface LayoutShift {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly from: Pick<LayoutCard, "x" | "y">;
|
|
18
|
+
readonly to: Pick<LayoutCard, "x" | "y">;
|
|
19
|
+
}
|
|
20
|
+
export declare function boundsOf(cards: readonly LayoutCard[]): Bounds;
|
|
21
|
+
/** Places a new frame to the right of every frame, on the first frame's row. */
|
|
22
|
+
export declare function nextCardPosition(cards: readonly LayoutCard[], gap: number): {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
};
|
|
26
|
+
export declare function pushOverlappingCards<T extends LayoutCard>(cards: readonly T[], protectedIds: ReadonlySet<string>, gap: number): T[];
|
|
27
|
+
export declare function iterationPosition(cards: readonly LayoutCard[], source: LayoutCard, gap: number): {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
};
|
|
31
|
+
/** Zooms and centers content inside the padded viewport; null for empty content. */
|
|
32
|
+
export declare function fitCamera(bounds: Bounds, viewport: {
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
}, padding: number): {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
zoom: number;
|
|
39
|
+
} | null;
|
|
40
|
+
export declare function centerCamera(bounds: Bounds, zoom: number, viewport: {
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
}): {
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
zoom: number;
|
|
47
|
+
};
|
|
48
|
+
export declare function layoutShifts(before: readonly LayoutCard[], after: readonly LayoutCard[]): LayoutShift[];
|
|
49
|
+
/** Replays recorded shifts as deltas, so frames moved since the creation keep their later moves. */
|
|
50
|
+
export declare function applyLayoutShifts<T extends LayoutCard>(cards: readonly T[], shifts: readonly LayoutShift[], forward: boolean): T[];
|
|
51
|
+
/** Reinserts recorded frames after their nearest recorded predecessor that still exists. */
|
|
52
|
+
export declare function reinsertInRecordedOrder<T extends LayoutCard>(cards: readonly T[], recorded: readonly T[], ids: ReadonlySet<string>): T[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { MAX_CAMERA_ZOOM, MIN_CAMERA_ZOOM } from "./viewStore.js";
|
|
2
|
+
export function boundsOf(cards) {
|
|
3
|
+
let minX = Infinity;
|
|
4
|
+
let minY = Infinity;
|
|
5
|
+
let maxX = -Infinity;
|
|
6
|
+
let maxY = -Infinity;
|
|
7
|
+
for (const card of cards) {
|
|
8
|
+
minX = Math.min(minX, card.x);
|
|
9
|
+
minY = Math.min(minY, card.y);
|
|
10
|
+
maxX = Math.max(maxX, card.x + card.width);
|
|
11
|
+
maxY = Math.max(maxY, card.y + card.height);
|
|
12
|
+
}
|
|
13
|
+
return { minX, minY, maxX, maxY };
|
|
14
|
+
}
|
|
15
|
+
/** Places a new frame to the right of every frame, on the first frame's row. */
|
|
16
|
+
export function nextCardPosition(cards, gap) {
|
|
17
|
+
if (cards.length === 0)
|
|
18
|
+
return { x: 0, y: 0 };
|
|
19
|
+
return { x: Math.max(...cards.map((card) => card.x + card.width + gap)), y: cards[0].y };
|
|
20
|
+
}
|
|
21
|
+
export function pushOverlappingCards(cards, protectedIds, gap) {
|
|
22
|
+
const fixed = cards.filter((card) => protectedIds.has(card.id)).map((card) => boundsOf([card]));
|
|
23
|
+
const moving = cards.filter((card) => !protectedIds.has(card.id)).sort((a, b) => a.x - b.x);
|
|
24
|
+
const shifts = new Map();
|
|
25
|
+
for (const card of moving) {
|
|
26
|
+
let bounds = boundsOf([card]);
|
|
27
|
+
let dx = 0;
|
|
28
|
+
let collision = true;
|
|
29
|
+
while (collision) {
|
|
30
|
+
collision = false;
|
|
31
|
+
for (const other of fixed) {
|
|
32
|
+
if (bounds.minY >= other.maxY || bounds.maxY <= other.minY
|
|
33
|
+
|| bounds.minX >= other.maxX + gap || bounds.maxX + gap <= other.minX)
|
|
34
|
+
continue;
|
|
35
|
+
const shift = other.maxX + gap - bounds.minX;
|
|
36
|
+
dx += shift;
|
|
37
|
+
bounds = { ...bounds, minX: bounds.minX + shift, maxX: bounds.maxX + shift };
|
|
38
|
+
collision = true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (dx)
|
|
42
|
+
shifts.set(card.id, dx);
|
|
43
|
+
fixed.push(bounds);
|
|
44
|
+
}
|
|
45
|
+
return shifts.size ? cards.map((card) => shifts.has(card.id) ? { ...card, x: card.x + shifts.get(card.id) } : card) : [...cards];
|
|
46
|
+
}
|
|
47
|
+
export function iterationPosition(cards, source, gap) {
|
|
48
|
+
const x = source.x;
|
|
49
|
+
let y = source.y + source.height + gap * 2;
|
|
50
|
+
for (const neighbor of [...cards].sort((a, b) => a.y - b.y)) {
|
|
51
|
+
if (x >= neighbor.x + neighbor.width + gap || x + source.width + gap <= neighbor.x)
|
|
52
|
+
continue;
|
|
53
|
+
if (y < neighbor.y + neighbor.height + gap * 2 && y + source.height + gap * 2 > neighbor.y)
|
|
54
|
+
y = neighbor.y + neighbor.height + gap * 2;
|
|
55
|
+
}
|
|
56
|
+
return { x, y };
|
|
57
|
+
}
|
|
58
|
+
/** Zooms and centers content inside the padded viewport; null for empty content. */
|
|
59
|
+
export function fitCamera(bounds, viewport, padding) {
|
|
60
|
+
const width = bounds.maxX - bounds.minX;
|
|
61
|
+
const height = bounds.maxY - bounds.minY;
|
|
62
|
+
if (width <= 0 || height <= 0)
|
|
63
|
+
return null;
|
|
64
|
+
const zoom = Math.max(MIN_CAMERA_ZOOM, Math.min(Math.max(1, viewport.width - padding * 2) / width, Math.max(1, viewport.height - padding * 2) / height, MAX_CAMERA_ZOOM));
|
|
65
|
+
return centerCamera(bounds, zoom, viewport);
|
|
66
|
+
}
|
|
67
|
+
export function centerCamera(bounds, zoom, viewport) {
|
|
68
|
+
return {
|
|
69
|
+
x: -((bounds.minX + bounds.maxX) / 2) * zoom + viewport.width / 2,
|
|
70
|
+
y: -((bounds.minY + bounds.maxY) / 2) * zoom + viewport.height / 2,
|
|
71
|
+
zoom,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function layoutShifts(before, after) {
|
|
75
|
+
const previous = new Map(before.map((card) => [card.id, card]));
|
|
76
|
+
return after.flatMap((card) => {
|
|
77
|
+
const old = previous.get(card.id);
|
|
78
|
+
if (!old || (old.x === card.x && old.y === card.y))
|
|
79
|
+
return [];
|
|
80
|
+
return [{ id: card.id, from: { x: old.x, y: old.y }, to: { x: card.x, y: card.y } }];
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** Replays recorded shifts as deltas, so frames moved since the creation keep their later moves. */
|
|
84
|
+
export function applyLayoutShifts(cards, shifts, forward) {
|
|
85
|
+
const byId = new Map(shifts.map((shift) => [shift.id, shift]));
|
|
86
|
+
return cards.map((card) => {
|
|
87
|
+
const shift = byId.get(card.id);
|
|
88
|
+
if (!shift)
|
|
89
|
+
return card;
|
|
90
|
+
const from = forward ? shift.from : shift.to;
|
|
91
|
+
const to = forward ? shift.to : shift.from;
|
|
92
|
+
return { ...card, x: card.x + to.x - from.x, y: card.y + to.y - from.y };
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** Reinserts recorded frames after their nearest recorded predecessor that still exists. */
|
|
96
|
+
export function reinsertInRecordedOrder(cards, recorded, ids) {
|
|
97
|
+
let next = [...cards];
|
|
98
|
+
for (const [index, added] of recorded.entries()) {
|
|
99
|
+
if (!ids.has(added.id))
|
|
100
|
+
continue;
|
|
101
|
+
const preceding = recorded.slice(0, index).reverse().find((card) => next.some((item) => item.id === card.id));
|
|
102
|
+
const insertion = preceding ? next.findIndex((card) => card.id === preceding.id) + 1 : 0;
|
|
103
|
+
next = [...next.slice(0, insertion), { ...added }, ...next.slice(insertion)];
|
|
104
|
+
}
|
|
105
|
+
return next;
|
|
106
|
+
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
export declare function findClosestAnchor(target: EventTarget | null): HTMLAnchorElement | null;
|
|
2
2
|
export declare function isEligibleNavigation(anchor: HTMLAnchorElement, event: MouseEvent): boolean;
|
|
3
3
|
export declare function hasDifferentRoute(anchor: HTMLAnchorElement): boolean;
|
|
4
|
-
/**
|
|
5
|
-
* Returns whether the renderer should leave an anchor's browser behavior
|
|
6
|
-
* intact instead of consuming it as an editing click.
|
|
7
|
-
*/
|
|
8
|
-
export declare function shouldPreserveNativeLinkActivation(anchor: HTMLAnchorElement, event: MouseEvent): boolean;
|
|
@@ -37,32 +37,3 @@ export function hasDifferentRoute(anchor) {
|
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Returns whether the renderer should leave an anchor's browser behavior
|
|
42
|
-
* intact instead of consuming it as an editing click.
|
|
43
|
-
*/
|
|
44
|
-
export function shouldPreserveNativeLinkActivation(anchor, event) {
|
|
45
|
-
// Command/Ctrl-click is the deep-selection gesture. Shift makes it the
|
|
46
|
-
// explicit application-activation gesture instead.
|
|
47
|
-
if (event.metaKey || event.ctrlKey)
|
|
48
|
-
return event.shiftKey;
|
|
49
|
-
// Shift-click is the additive inspector-selection gesture. Do not let a
|
|
50
|
-
// link navigate or preserve its native activation in that mode.
|
|
51
|
-
if (event.shiftKey)
|
|
52
|
-
return false;
|
|
53
|
-
if (anchor.hasAttribute("download"))
|
|
54
|
-
return true;
|
|
55
|
-
if (anchor.target && anchor.target !== "" && anchor.target !== "_self")
|
|
56
|
-
return true;
|
|
57
|
-
if (anchor.protocol !== "http:" && anchor.protocol !== "https:")
|
|
58
|
-
return true;
|
|
59
|
-
try {
|
|
60
|
-
const url = new URL(anchor.href);
|
|
61
|
-
if (url.origin !== window.location.origin)
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
return !hasDifferentRoute(anchor);
|
|
68
|
-
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type DraftContents } from "../changes/draftChanges.js";
|
|
2
2
|
import type { CanvasCard } from "./canvasStore.js";
|
|
3
3
|
import { type PreviewDocument } from "../changes/previewDiagnostics.js";
|
|
4
|
-
import { type CompiledManagedStyles } from "../projection/
|
|
4
|
+
import { type CompiledManagedStyles, type DraftProjectionPlan } from "../projection/draftProjection.js";
|
|
5
5
|
export declare const PROJECT_ID: string;
|
|
6
|
-
export declare const WORKSPACE_ID:
|
|
6
|
+
export declare const WORKSPACE_ID: string;
|
|
7
|
+
export interface FrameProjection extends DraftProjectionPlan<CompiledManagedStyles> {
|
|
8
|
+
readonly draftId: string;
|
|
9
|
+
readonly draftRevision: number;
|
|
10
|
+
readonly revision: number;
|
|
11
|
+
readonly css: string;
|
|
12
|
+
}
|
|
7
13
|
export interface CanvasProjectionStatus {
|
|
8
14
|
sentRevision: number;
|
|
9
15
|
appliedRevision: number;
|
|
@@ -22,10 +28,14 @@ export declare function computeProjection(): {
|
|
|
22
28
|
textContentChanges: readonly import("../changes/editModel.js").TextContentChangeRecord[];
|
|
23
29
|
componentOverrides: readonly import("../componentSemantics/types.js").ComponentOverride[];
|
|
24
30
|
};
|
|
31
|
+
export declare function computeProjectionForCard(cardId: string): FrameProjection;
|
|
32
|
+
export declare function getCanonicalFrameProjection(cardId: string, expectedRevision: number): FrameProjection | null;
|
|
33
|
+
export declare function restoreCanonicalFrameProjection(doc: Document): void;
|
|
34
|
+
export declare function getCanvasCardIdForDocument(doc: Document): string | null;
|
|
25
35
|
export declare function resetProjectionRevision(): void;
|
|
26
|
-
export declare function sendProjectionToCard(card: CanvasCard, iframe: HTMLIFrameElement): void;
|
|
36
|
+
export declare function sendProjectionToCard(card: Pick<CanvasCard, "id">, iframe: HTMLIFrameElement): void;
|
|
27
37
|
/** Temporarily projects a snapshot through the renderer that owns the document. */
|
|
28
|
-
export declare function
|
|
38
|
+
export declare function projectDraftToDocument(doc: Document, snapshot: DraftContents): Promise<number | null>;
|
|
29
39
|
/** Returns whether an acknowledged temporary projection is still authoritative. */
|
|
30
40
|
export declare function isCanvasProjectionRevisionCurrent(doc: Document, expectedRevision: number): boolean;
|
|
31
41
|
/** Returns whether the live frame acknowledged its latest canonical workspace projection. */
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { draftIdForCard } from "../drafts/store.js";
|
|
2
|
+
import { getDraftChanges, getActiveDraftChanges } from "../changes/draftChanges.js";
|
|
3
|
+
import { getCanvasCards } from "./canvasStore.js";
|
|
4
|
+
import { getCanvasMode } from "./viewStore.js";
|
|
3
5
|
import { PROTOCOL_VERSION } from "./frameProtocol.js";
|
|
4
6
|
import { clearCanvasRenderedInstanceProjectionReports, setCanonicalRenderedInstanceOverrides, } from "../projection/renderedInstance.js";
|
|
5
7
|
import { clearCanvasTextProjectionReports, setCanonicalTextContentChanges, } from "../projection/textProjection.js";
|
|
6
8
|
import { clearCanvasStructuralProjectionReports } from "../projection/structuralProjection.js";
|
|
7
9
|
import { invalidatePreviewDocumentSession, startPreviewDocumentSession, } from "../changes/previewDiagnostics.js";
|
|
8
|
-
import {
|
|
10
|
+
import { compileDraftProjection, } from "../projection/draftProjection.js";
|
|
9
11
|
import { isOriginalPreviewActive } from "../shell/originalPreview.js";
|
|
10
12
|
import { disposeBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
|
|
11
|
-
export const PROJECT_ID = window.location
|
|
12
|
-
|
|
13
|
+
export const PROJECT_ID = typeof window !== "undefined" && window.location?.origin
|
|
14
|
+
? window.location.origin
|
|
15
|
+
: "";
|
|
16
|
+
export const WORKSPACE_ID = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
|
|
17
|
+
? crypto.randomUUID()
|
|
18
|
+
: `ws-${Date.now()}`;
|
|
13
19
|
let revision = 0;
|
|
14
20
|
let lastRulesKey = null;
|
|
15
21
|
let lastPeekActive = false;
|
|
22
|
+
const draftPlans = new Map();
|
|
16
23
|
const frameProjectionStates = new Map();
|
|
17
24
|
const previewDocuments = new Map();
|
|
18
25
|
const projectionAcknowledgementListeners = new Set();
|
|
@@ -22,6 +29,7 @@ function createPreviewDocumentIdentity(cardId) {
|
|
|
22
29
|
const previewDocument = {
|
|
23
30
|
logicalDocument: `canvas:${cardId}`,
|
|
24
31
|
sessionId: `canvas-session-${nextPreviewSessionId++}`,
|
|
32
|
+
draftId: draftIdForCard(cardId) ?? getActiveDraftChanges().draftId,
|
|
25
33
|
};
|
|
26
34
|
previewDocuments.set(cardId, previewDocument);
|
|
27
35
|
return previewDocument;
|
|
@@ -50,7 +58,7 @@ function projectionKey(plan) {
|
|
|
50
58
|
return `${plan.managedStyles.css}\u0000${JSON.stringify(plan.instanceOverrides)}\u0000${JSON.stringify(plan.structuralChanges)}\u0000${JSON.stringify(plan.textContentChanges)}\u0000${JSON.stringify(plan.componentOverrides)}`;
|
|
51
59
|
}
|
|
52
60
|
export function computeProjection() {
|
|
53
|
-
const plan =
|
|
61
|
+
const plan = compileDraftProjection(getActiveDraftChanges());
|
|
54
62
|
if (isOriginalPreviewActive()) {
|
|
55
63
|
// Hold-to-view-original: frames show the page without inspector changes
|
|
56
64
|
// while canonical intent (and its projection key) stays untouched.
|
|
@@ -84,14 +92,53 @@ export function computeProjection() {
|
|
|
84
92
|
}
|
|
85
93
|
return { ...plan, css: plan.managedStyles.css, revision };
|
|
86
94
|
}
|
|
95
|
+
export function computeProjectionForCard(cardId) {
|
|
96
|
+
const draftId = draftIdForCard(cardId);
|
|
97
|
+
const workspace = draftId ? getDraftChanges(draftId) : getActiveDraftChanges();
|
|
98
|
+
const peek = isOriginalPreviewActive();
|
|
99
|
+
const cached = draftPlans.get(workspace.draftId);
|
|
100
|
+
if (cached && cached.plan.draftRevision === workspace.revision && cached.peek === peek)
|
|
101
|
+
return cached.plan;
|
|
102
|
+
const source = peek ? { ...workspace, changes: [], structuralChanges: [] } : workspace;
|
|
103
|
+
const compiled = compileDraftProjection(source);
|
|
104
|
+
const plan = { ...compiled, draftId: workspace.draftId, draftRevision: workspace.revision, css: compiled.managedStyles.css, revision: ++revision };
|
|
105
|
+
draftPlans.set(workspace.draftId, { peek, plan });
|
|
106
|
+
return plan;
|
|
107
|
+
}
|
|
108
|
+
export function getCanonicalFrameProjection(cardId, expectedRevision) {
|
|
109
|
+
const state = frameProjectionStates.get(cardId);
|
|
110
|
+
const plan = state?.canonicalProjection;
|
|
111
|
+
const draftId = draftIdForCard(cardId);
|
|
112
|
+
const draft = draftId ? getDraftChanges(draftId) : null;
|
|
113
|
+
if (!state || !previewDocuments.has(cardId) || state.iframe.contentDocument !== state.document
|
|
114
|
+
|| !plan || plan.revision !== expectedRevision || state.sentRevision !== expectedRevision
|
|
115
|
+
|| (draft && (draft.draftId !== plan.draftId || draft.revision !== plan.draftRevision)))
|
|
116
|
+
return null;
|
|
117
|
+
return plan;
|
|
118
|
+
}
|
|
119
|
+
export function restoreCanonicalFrameProjection(doc) {
|
|
120
|
+
const id = getCanvasCardIdForDocument(doc);
|
|
121
|
+
const card = getCanvasCards().find((card) => card.id === id);
|
|
122
|
+
const frame = id ? frameRegistry.get(id) : undefined;
|
|
123
|
+
if (card && frame)
|
|
124
|
+
sendProjectionToCard(card, frame);
|
|
125
|
+
}
|
|
126
|
+
export function getCanvasCardIdForDocument(doc) {
|
|
127
|
+
for (const [id, state] of frameProjectionStates)
|
|
128
|
+
if (state.document === doc && state.iframe.contentDocument === doc)
|
|
129
|
+
return id;
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
87
132
|
export function resetProjectionRevision() {
|
|
88
133
|
revision = 0;
|
|
89
134
|
lastRulesKey = null;
|
|
90
135
|
lastPeekActive = false;
|
|
136
|
+
draftPlans.clear();
|
|
91
137
|
for (const state of frameProjectionStates.values()) {
|
|
92
138
|
state.sentRevision = -1;
|
|
93
139
|
state.appliedRevision = -1;
|
|
94
140
|
state.canonicalSentRevision = -1;
|
|
141
|
+
state.canonicalProjection = null;
|
|
95
142
|
}
|
|
96
143
|
projectionAcknowledgementVersion += 1;
|
|
97
144
|
notifyProjectionAcknowledgementListeners();
|
|
@@ -100,7 +147,15 @@ export function sendProjectionToCard(card, iframe) {
|
|
|
100
147
|
const win = iframe.contentWindow;
|
|
101
148
|
if (!win)
|
|
102
149
|
return;
|
|
103
|
-
|
|
150
|
+
let plan = computeProjectionForCard(card.id);
|
|
151
|
+
const state = frameProjectionStates.get(card.id);
|
|
152
|
+
if (state && plan.revision < state.sentRevision) {
|
|
153
|
+
plan = { ...plan, revision: ++revision };
|
|
154
|
+
draftPlans.set(plan.draftId, { peek: isOriginalPreviewActive(), plan });
|
|
155
|
+
}
|
|
156
|
+
if (state)
|
|
157
|
+
state.canonicalProjection = plan;
|
|
158
|
+
const { css, revision: rev, instanceOverrides, structuralChanges, textContentChanges, componentOverrides } = plan;
|
|
104
159
|
sendProjectionMessage(card.id, iframe, rev, css, instanceOverrides, structuralChanges, textContentChanges, componentOverrides, true);
|
|
105
160
|
}
|
|
106
161
|
function sendProjectionMessage(cardId, iframe, rev, css, instanceOverrides, structuralChanges, textContentChanges, componentOverrides, canonical = false) {
|
|
@@ -124,12 +179,12 @@ function sendProjectionMessage(cardId, iframe, rev, css, instanceOverrides, stru
|
|
|
124
179
|
win.postMessage(msg, window.location.origin);
|
|
125
180
|
}
|
|
126
181
|
/** Temporarily projects a snapshot through the renderer that owns the document. */
|
|
127
|
-
export async function
|
|
182
|
+
export async function projectDraftToDocument(doc, snapshot) {
|
|
128
183
|
const entry = [...frameProjectionStates.entries()].find(([, state]) => state.document === doc);
|
|
129
184
|
if (!entry)
|
|
130
185
|
return null;
|
|
131
186
|
const [cardId, state] = entry;
|
|
132
|
-
const plan =
|
|
187
|
+
const plan = compileDraftProjection({ ...snapshot, revision: 0 });
|
|
133
188
|
revision += 1;
|
|
134
189
|
sendProjectionMessage(cardId, state.iframe, revision, plan.managedStyles.css, plan.instanceOverrides, plan.structuralChanges, plan.textContentChanges, plan.componentOverrides);
|
|
135
190
|
if (state.appliedRevision === revision && state.sentRevision === revision)
|
|
@@ -203,6 +258,8 @@ export function registerCardFrame(cardId, iframe) {
|
|
|
203
258
|
const existing = frameProjectionStates.get(cardId);
|
|
204
259
|
if (!existing || existing.iframe !== iframe || existing.document !== frameDocument) {
|
|
205
260
|
if (existing) {
|
|
261
|
+
if (existing.canonicalProjection)
|
|
262
|
+
draftPlans.delete(existing.canonicalProjection.draftId);
|
|
206
263
|
invalidatePreviewDocumentSession(existing.previewDocument.logicalDocument, existing.previewDocument.sessionId);
|
|
207
264
|
previewDocuments.delete(cardId);
|
|
208
265
|
}
|
|
@@ -215,6 +272,7 @@ export function registerCardFrame(cardId, iframe) {
|
|
|
215
272
|
sentRevision: -1,
|
|
216
273
|
appliedRevision: -1,
|
|
217
274
|
canonicalSentRevision: -1,
|
|
275
|
+
canonicalProjection: null,
|
|
218
276
|
});
|
|
219
277
|
}
|
|
220
278
|
else {
|
|
@@ -318,8 +376,6 @@ function notifyFrameRegistryListeners() {
|
|
|
318
376
|
export function projectToAllReadyCards() {
|
|
319
377
|
if (getCanvasMode() !== "canvas")
|
|
320
378
|
return;
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
sendProjectionMessage(cardId, iframe, rev, css, instanceOverrides, structuralChanges, textContentChanges, componentOverrides, true);
|
|
324
|
-
}
|
|
379
|
+
for (const [id, iframe] of frameRegistry)
|
|
380
|
+
sendProjectionToCard({ id }, iframe);
|
|
325
381
|
}
|