nudge-ui 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/bin/nudge-ui.mjs +3 -1
  2. package/dist/cli/agentCommand.d.ts +8 -0
  3. package/dist/cli/agentCommand.js +59 -0
  4. package/dist/client.mjs +872 -12
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
  6. package/dist/hosts/astro/clientTransport.js +60 -11
  7. package/dist/hosts/next/sidecar.d.ts +4 -0
  8. package/dist/hosts/next/sidecar.js +49 -6
  9. package/dist/hosts/next/wrapper.js +30 -2
  10. package/dist/hosts/projectBridge.d.ts +20 -0
  11. package/dist/hosts/projectBridge.js +97 -0
  12. package/dist/hosts/static/cli.d.ts +1 -1
  13. package/dist/hosts/static/cli.js +6 -1
  14. package/dist/hosts/static/manifest.d.ts +1 -1
  15. package/dist/hosts/static/manifest.js +1 -1
  16. package/dist/hosts/static/server.js +44 -3
  17. package/dist/hosts/vite/vite.d.ts +5 -1
  18. package/dist/hosts/vite/vite.js +78 -14
  19. package/dist/inspector/agent/McpConnectionDialog.d.ts +2 -2
  20. package/dist/inspector/agent/McpConnectionDialog.js +11 -35
  21. package/dist/inspector/agent/client.d.ts +10 -3
  22. package/dist/inspector/agent/client.js +80 -9
  23. package/dist/inspector/agent/connectionStatus.js +3 -3
  24. package/dist/inspector/agent/httpTransport.d.ts +2 -0
  25. package/dist/inspector/agent/httpTransport.js +37 -17
  26. package/dist/inspector/agent/protocol.d.ts +8 -3
  27. package/dist/inspector/agent/protocol.js +1 -1
  28. package/dist/inspector/agent/verification.d.ts +1 -1
  29. package/dist/inspector/agent/verification.js +69 -12
  30. package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
  31. package/dist/inspector/canvas/CanvasCard.js +181 -91
  32. package/dist/inspector/canvas/CanvasElementOverlay.js +246 -52
  33. package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
  34. package/dist/inspector/canvas/CanvasToolbar.js +24 -0
  35. package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
  36. package/dist/inspector/canvas/CanvasWorkspace.js +392 -73
  37. package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
  38. package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
  39. package/dist/inspector/canvas/canvasResize.d.ts +30 -0
  40. package/dist/inspector/canvas/canvasResize.js +52 -0
  41. package/dist/inspector/canvas/canvasStore.d.ts +30 -6
  42. package/dist/inspector/canvas/canvasStore.js +128 -61
  43. package/dist/inspector/canvas/frameProtocol.d.ts +69 -5
  44. package/dist/inspector/canvas/frameProtocol.js +56 -2
  45. package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -0
  46. package/dist/inspector/canvas/keyboardShortcuts.js +14 -0
  47. package/dist/inspector/canvas/projection.d.ts +7 -0
  48. package/dist/inspector/canvas/projection.js +118 -25
  49. package/dist/inspector/canvas/rendererBootstrap.js +29 -29
  50. package/dist/inspector/canvas/rendererElementSelector.js +341 -30
  51. package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
  52. package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
  53. package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
  54. package/dist/inspector/canvas/sessionStore.d.ts +1 -0
  55. package/dist/inspector/canvas/sessionStore.js +12 -4
  56. package/dist/inspector/canvas/staleChangeDetector.js +16 -14
  57. package/dist/inspector/changes/changesLog.js +31 -1
  58. package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
  59. package/dist/inspector/changes/managedStyleProjection.js +3 -3
  60. package/dist/inspector/changes/structuralTypes.d.ts +4 -0
  61. package/dist/inspector/clientManifest.d.ts +5 -0
  62. package/dist/inspector/clientManifest.js +11 -0
  63. package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
  64. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
  65. package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
  66. package/dist/inspector/index.d.ts +2 -0
  67. package/dist/inspector/index.js +74 -24
  68. package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
  69. package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
  70. package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
  71. package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
  72. package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
  73. package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
  74. package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
  75. package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
  76. package/dist/inspector/inline-text/textEditChange.js +165 -0
  77. package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
  78. package/dist/inspector/inspection/selectionProperty.js +1 -0
  79. package/dist/inspector/overlay/selectedGeometry.js +21 -2
  80. package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
  81. package/dist/inspector/overlay/spacingGestures.js +353 -0
  82. package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
  83. package/dist/inspector/overlay/structuralGestures.js +12 -10
  84. package/dist/inspector/projection/managedStylesheet.js +4 -1
  85. package/dist/inspector/projection/renderedInstance.d.ts +2 -0
  86. package/dist/inspector/projection/renderedInstance.js +5 -1
  87. package/dist/inspector/projection/structuralProjection.d.ts +2 -0
  88. package/dist/inspector/projection/structuralProjection.js +85 -1
  89. package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
  90. package/dist/inspector/projection/textProjection.d.ts +2 -0
  91. package/dist/inspector/projection/textProjection.js +10 -4
  92. package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
  93. package/dist/inspector/prompt/clipboardHandoff.js +3 -3
  94. package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
  95. package/dist/inspector/prompt/generatePrompt.js +19 -2
  96. package/dist/inspector/runtime/editorShell.d.ts +2 -0
  97. package/dist/inspector/runtime/editorShell.js +4 -0
  98. package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
  99. package/dist/inspector/runtime/runtimeConfig.js +4 -0
  100. package/dist/inspector/selection/selectionStore.js +2 -0
  101. package/dist/inspector/settings/SettingsDialog.js +10 -0
  102. package/dist/inspector/shell/AppShell.js +5 -1
  103. package/dist/inspector/shell/ChangesLog.js +8 -4
  104. package/dist/inspector/shell/CopyPromptButton.js +95 -19
  105. package/dist/inspector/shell/EmptyState.js +1 -1
  106. package/dist/inspector/shell/InspectorShell.d.ts +0 -1
  107. package/dist/inspector/shell/InspectorShell.js +37 -38
  108. package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
  109. package/dist/inspector/shell/PeekOriginalButton.js +57 -0
  110. package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
  111. package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
  112. package/dist/inspector/shell/originalPreview.d.ts +9 -0
  113. package/dist/inspector/shell/originalPreview.js +61 -0
  114. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  115. package/dist/inspector/shell/shortcuts.js +15 -0
  116. package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
  117. package/dist/inspector/sketch/SketchChanges.js +72 -0
  118. package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
  119. package/dist/inspector/sketch/SketchEditor.js +402 -0
  120. package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
  121. package/dist/inspector/sketch/SketchEntryButton.js +11 -0
  122. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
  123. package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
  124. package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
  125. package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
  126. package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
  127. package/dist/inspector/sketch/SketchOverlay.js +420 -0
  128. package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
  129. package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
  130. package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
  131. package/dist/inspector/sketch/SketchWorkspace.js +152 -0
  132. package/dist/inspector/sketch/capture.d.ts +55 -0
  133. package/dist/inspector/sketch/capture.js +350 -0
  134. package/dist/inspector/sketch/freehand.d.ts +13 -0
  135. package/dist/inspector/sketch/freehand.js +64 -0
  136. package/dist/inspector/sketch/geometry.d.ts +17 -0
  137. package/dist/inspector/sketch/geometry.js +98 -0
  138. package/dist/inspector/sketch/handoff.d.ts +21 -0
  139. package/dist/inspector/sketch/handoff.js +72 -0
  140. package/dist/inspector/sketch/interaction.d.ts +21 -0
  141. package/dist/inspector/sketch/interaction.js +98 -0
  142. package/dist/inspector/sketch/model.d.ts +104 -0
  143. package/dist/inspector/sketch/model.js +282 -0
  144. package/dist/inspector/sketch/persistence.d.ts +21 -0
  145. package/dist/inspector/sketch/persistence.js +205 -0
  146. package/dist/inspector/sketch/raster.d.ts +22 -0
  147. package/dist/inspector/sketch/raster.js +203 -0
  148. package/dist/inspector/sketch/sketchNote.d.ts +10 -0
  149. package/dist/inspector/sketch/sketchNote.js +30 -0
  150. package/dist/inspector/sketch/store.d.ts +47 -0
  151. package/dist/inspector/sketch/store.js +409 -0
  152. package/dist/inspector/styleEditors/BorderEditor.js +15 -19
  153. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
  154. package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
  155. package/dist/inspector/styleEditors/ColorPicker.js +11 -36
  156. package/dist/inspector/styleEditors/GapField.d.ts +3 -2
  157. package/dist/inspector/styleEditors/GapField.js +2 -2
  158. package/dist/inspector/styleEditors/GridSection.js +1 -1
  159. package/dist/inspector/styleEditors/LayoutSection.js +11 -8
  160. package/dist/inspector/styleEditors/SpacingBox.js +23 -30
  161. package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
  162. package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
  163. package/dist/inspector/styleEditors/layoutValue.js +3 -0
  164. package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
  165. package/dist/inspector/styleEditors/nudgeValue.js +91 -5
  166. package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
  167. package/dist/inspector/styleEditors/stylePresence.js +22 -0
  168. package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
  169. package/dist/inspector/styleEditors/textRelevance.js +35 -0
  170. package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
  171. package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
  172. package/dist/inspector/tokens/TokenField.js +111 -7
  173. package/dist/inspector/tokens/editActions.d.ts +2 -0
  174. package/dist/inspector/tokens/editActions.js +2 -2
  175. package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
  176. package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
  177. package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
  178. package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
  179. package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
  180. package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
  181. package/dist/inspector/tokens/resolution.d.ts +3 -26
  182. package/dist/inspector/tokens/resolution.js +44 -735
  183. package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
  184. package/dist/inspector/tokens/tokenAvailability.js +204 -0
  185. package/dist/inspector/ui/Button.d.ts +1 -1
  186. package/dist/inspector/ui/SideValuesField.js +2 -2
  187. package/dist/inspector/ui/StatusCallout.d.ts +4 -1
  188. package/dist/inspector/ui/StatusCallout.js +2 -2
  189. package/dist/inspector/ui/styles.js +13 -11
  190. package/dist/internal/agent-protocol.d.ts +65 -1
  191. package/dist/internal/agent-protocol.js +183 -3
  192. package/dist/nudge-ui.mjs +261 -57
  193. package/dist/transport/editor.d.ts +28 -0
  194. package/dist/transport/editor.js +132 -0
  195. package/dist/transport/index.d.ts +1 -0
  196. package/dist/transport/index.js +1 -0
  197. package/dist/transport/routes.d.ts +7 -0
  198. package/dist/transport/routes.js +7 -0
  199. package/package.json +3 -1
  200. package/dist/inspector/shell/panelLayout.d.ts +0 -2
  201. package/dist/inspector/shell/panelLayout.js +0 -29
@@ -0,0 +1,23 @@
1
+ import { getFocusedCardId, getSelectedCardId } from "./canvasStore.js";
2
+ import { getRegisteredFrames } from "./projection.js";
3
+ /** Resolves only the selected or focused frame. */
4
+ export function resolveActiveCanvasFrame(frames, selectedCardId, focusedCardId) {
5
+ const activeCardId = selectedCardId ?? focusedCardId;
6
+ return activeCardId ? frames.get(activeCardId) ?? null : null;
7
+ }
8
+ export function getActiveCanvasDocument() {
9
+ const frame = getActiveCanvasFrame();
10
+ if (!frame)
11
+ return null;
12
+ try {
13
+ return frame.contentDocument;
14
+ }
15
+ catch {
16
+ // Cross-origin frames cannot supply trusted document data.
17
+ return null;
18
+ }
19
+ }
20
+ /** Returns the iframe that owns the active renderer document. */
21
+ export function getActiveCanvasFrame() {
22
+ return resolveActiveCanvasFrame(getRegisteredFrames(), getSelectedCardId(), getFocusedCardId());
23
+ }
@@ -0,0 +1,30 @@
1
+ export declare const MIN_CARD_WIDTH = 200;
2
+ export declare const MIN_CARD_HEIGHT = 150;
3
+ export type CanvasResizeDirection = "top-left" | "top" | "top-right" | "right" | "bottom-right" | "bottom" | "bottom-left" | "left";
4
+ export interface CanvasResizeRect {
5
+ readonly x: number;
6
+ readonly y: number;
7
+ readonly width: number;
8
+ readonly height: number;
9
+ }
10
+ export interface CanvasResizeDelta {
11
+ readonly x: number;
12
+ readonly y: number;
13
+ }
14
+ export interface CanvasResizeMinimum {
15
+ readonly width: number;
16
+ readonly height: number;
17
+ }
18
+ interface ResizeEdges {
19
+ readonly horizontal: "left" | "right" | null;
20
+ readonly vertical: "top" | "bottom" | null;
21
+ }
22
+ /** Returns the anchored edges affected by a resize handle. */
23
+ export declare function getCanvasResizeEdges(direction: CanvasResizeDirection): ResizeEdges;
24
+ /**
25
+ * Applies a pointer delta to a Canvas card while keeping the opposite edges
26
+ * fixed. Minimum dimensions are clamped at the moving edge, so dragging past
27
+ * the minimum does not move the anchored side.
28
+ */
29
+ export declare function resizeCanvasRect(rect: CanvasResizeRect, direction: CanvasResizeDirection, delta: CanvasResizeDelta, minimum?: CanvasResizeMinimum): CanvasResizeRect;
30
+ export {};
@@ -0,0 +1,52 @@
1
+ export const MIN_CARD_WIDTH = 200;
2
+ export const MIN_CARD_HEIGHT = 150;
3
+ const RESIZE_EDGES = {
4
+ "top-left": { horizontal: "left", vertical: "top" },
5
+ top: { horizontal: null, vertical: "top" },
6
+ "top-right": { horizontal: "right", vertical: "top" },
7
+ right: { horizontal: "right", vertical: null },
8
+ "bottom-right": { horizontal: "right", vertical: "bottom" },
9
+ bottom: { horizontal: null, vertical: "bottom" },
10
+ "bottom-left": { horizontal: "left", vertical: "bottom" },
11
+ left: { horizontal: "left", vertical: null },
12
+ };
13
+ /** Returns the anchored edges affected by a resize handle. */
14
+ export function getCanvasResizeEdges(direction) {
15
+ return RESIZE_EDGES[direction];
16
+ }
17
+ /**
18
+ * Applies a pointer delta to a Canvas card while keeping the opposite edges
19
+ * fixed. Minimum dimensions are clamped at the moving edge, so dragging past
20
+ * the minimum does not move the anchored side.
21
+ */
22
+ export function resizeCanvasRect(rect, direction, delta, minimum = { width: MIN_CARD_WIDTH, height: MIN_CARD_HEIGHT }) {
23
+ const edges = getCanvasResizeEdges(direction);
24
+ const minWidth = Math.max(0, minimum.width);
25
+ const minHeight = Math.max(0, minimum.height);
26
+ const initialWidth = Math.max(rect.width, minWidth);
27
+ const initialHeight = Math.max(rect.height, minHeight);
28
+ const initialRight = rect.x + initialWidth;
29
+ const initialBottom = rect.y + initialHeight;
30
+ let left = rect.x;
31
+ let right = initialRight;
32
+ let top = rect.y;
33
+ let bottom = initialBottom;
34
+ if (edges.horizontal === "left") {
35
+ left = Math.min(initialRight - minWidth, rect.x + delta.x);
36
+ }
37
+ else if (edges.horizontal === "right") {
38
+ right = Math.max(rect.x + minWidth, initialRight + delta.x);
39
+ }
40
+ if (edges.vertical === "top") {
41
+ top = Math.min(initialBottom - minHeight, rect.y + delta.y);
42
+ }
43
+ else if (edges.vertical === "bottom") {
44
+ bottom = Math.max(rect.y + minHeight, initialBottom + delta.y);
45
+ }
46
+ return {
47
+ x: left,
48
+ y: top,
49
+ width: right - left,
50
+ height: bottom - top,
51
+ };
52
+ }
@@ -1,8 +1,11 @@
1
1
  import { type NormalizedUrl } from "./normalizeUrl.js";
2
2
  export type CanvasMode = "inspect" | "canvas";
3
+ export type CanvasPresentation = "focus" | "canvas";
3
4
  export interface CanvasCard {
4
5
  id: string;
5
6
  url: string;
7
+ /** A controller-requested document load. Renderer metadata must not set this. */
8
+ navigationUrl?: string;
6
9
  title: string | null;
7
10
  x: number;
8
11
  y: number;
@@ -36,15 +39,23 @@ export declare const MIN_CAMERA_ZOOM = 0.25;
36
39
  export declare const MAX_CAMERA_ZOOM = 3;
37
40
  export declare const CARD_GAP = 40;
38
41
  export declare const FIT_ALL_PADDING = 80;
42
+ export declare const PRIMARY_CARD_INSET = 40;
39
43
  declare function subscribe(cb: () => void): () => void;
40
44
  declare function getMode(): CanvasMode;
41
45
  declare function getCards(): CanvasCard[];
42
46
  declare function getComparisonGroups(): CanvasComparisonGroup[];
43
47
  export declare function setCanvasMode(newMode: CanvasMode): void;
44
- export declare function enterCanvas(): void;
45
- export declare function exitCanvas(): void;
46
- /** Return to inspect mode on a canvas card's route. */
47
- export declare function exitCanvasToCard(card: CanvasCard): void;
48
+ /**
49
+ * Changes how the mounted iframe workspace is presented.
50
+ *
51
+ * Canvas starts from the active card centered in the supplied viewport. Card
52
+ * geometry remains durable, but the camera does not carry over from a prior
53
+ * Canvas session.
54
+ */
55
+ export declare function setCanvasPresentation(next: CanvasPresentation, viewport?: {
56
+ width: number;
57
+ height: number;
58
+ }): void;
48
59
  /** Adds one card while retaining the existing placement policy. */
49
60
  export declare function addCanvasCard(url: string, title?: string): CanvasCard;
50
61
  /**
@@ -68,10 +79,21 @@ export declare function removeCanvasComparisonGroup(id: string): CanvasCompariso
68
79
  /** Clears group metadata while leaving ordinary cards intact. */
69
80
  export declare function clearCanvasComparisonGroups(): void;
70
81
  export declare function removeCanvasCard(id: string): void;
82
+ /**
83
+ * Opens one editor target in the iframe workspace without replacing restored
84
+ * cards or their dimensions.
85
+ */
86
+ export declare function activateIframeWorkspace(url: string, viewport: {
87
+ width: number;
88
+ height: number;
89
+ }): CanvasCard | null;
71
90
  export declare function updateCardTitle(id: string, title: string): void;
72
91
  export declare function updateCardUrl(id: string, url: string): void;
73
92
  export declare function duplicateCard(sourceId: string): CanvasCard | null;
74
- export declare function resizeCard(id: string, width: number, height: number): void;
93
+ export declare function resizeCard(id: string, width: number, height: number, position?: {
94
+ readonly x: number;
95
+ readonly y: number;
96
+ }): void;
75
97
  export declare function getBoardCamera(): CanvasCamera;
76
98
  export declare function setBoardCamera(camera: CanvasCamera): void;
77
99
  export declare function updateBoardCamera(partial: Partial<CanvasCamera>): void;
@@ -100,9 +122,11 @@ export declare function getSelectedCardId(): string | null;
100
122
  export declare function useSelectedCardId(): string | null;
101
123
  export declare function useFocusedCardId(): string | null;
102
124
  export declare function setCardPosition(id: string, x: number, y: number): void;
103
- export declare function hydrateCanvasStore(newMode: CanvasMode, newCards: CanvasCard[], newCamera: CanvasCamera, newComparisonGroups?: CanvasComparisonGroup[]): void;
125
+ export declare function hydrateCanvasStore(newMode: CanvasMode, newCards: CanvasCard[], newCamera: CanvasCamera, newComparisonGroups?: CanvasComparisonGroup[], restoredFocusedCardId?: string | null): void;
104
126
  export { subscribe, getMode, getCards, getComparisonGroups, getMode as getCanvasMode, getCards as getCanvasCards, getComparisonGroups as getCanvasComparisonGroups, };
105
127
  export declare function useCanvasMode(): CanvasMode;
128
+ export declare function useCanvasPresentation(): CanvasPresentation;
129
+ export declare function useCanvasPresentationTransitioning(): boolean;
106
130
  export declare function useCanvasCards(): CanvasCard[];
107
131
  export declare function useCanvasComparisonGroups(): CanvasComparisonGroup[];
108
132
  export declare function useBoardCamera(): CanvasCamera;
@@ -4,6 +4,7 @@ export const MIN_CAMERA_ZOOM = 0.25;
4
4
  export const MAX_CAMERA_ZOOM = 3;
5
5
  export const CARD_GAP = 40;
6
6
  export const FIT_ALL_PADDING = 80;
7
+ export const PRIMARY_CARD_INSET = 40;
7
8
  const DEFAULT_CAMERA = { x: 0, y: 0, zoom: 1 };
8
9
  function defaultViewportSize() {
9
10
  return {
@@ -12,6 +13,9 @@ function defaultViewportSize() {
12
13
  };
13
14
  }
14
15
  let mode = "inspect";
16
+ let presentation = "focus";
17
+ let presentationTransitioning = false;
18
+ let presentationTransitionTimer = null;
15
19
  let cards = [];
16
20
  let comparisonGroups = [];
17
21
  let focusedCardId = null;
@@ -30,6 +34,12 @@ function subscribe(cb) {
30
34
  function getMode() {
31
35
  return mode;
32
36
  }
37
+ function getPresentation() {
38
+ return presentation;
39
+ }
40
+ function getPresentationTransitioning() {
41
+ return presentationTransitioning;
42
+ }
33
43
  function getCards() {
34
44
  return cards;
35
45
  }
@@ -70,53 +80,45 @@ export function setCanvasMode(newMode) {
70
80
  mode = newMode;
71
81
  notify();
72
82
  }
73
- export function enterCanvas() {
74
- if (mode === "canvas")
83
+ /**
84
+ * Changes how the mounted iframe workspace is presented.
85
+ *
86
+ * Canvas starts from the active card centered in the supplied viewport. Card
87
+ * geometry remains durable, but the camera does not carry over from a prior
88
+ * Canvas session.
89
+ */
90
+ export function setCanvasPresentation(next, viewport) {
91
+ if (presentation === next)
75
92
  return;
76
- mode = "canvas";
77
- if (cards.length === 0) {
78
- const size = lastUsedCardSize ?? defaultViewportSize();
79
- const card = {
80
- id: `card-${++cardIdCounter}`,
81
- url: window.location.href,
82
- title: document.title,
83
- x: 0,
84
- y: 0,
85
- width: size.width,
86
- height: size.height,
87
- };
88
- cards = [card];
89
- lastUsedCardSize = { width: size.width, height: size.height };
90
- cachedBoardCamera = { ...DEFAULT_CAMERA };
91
- fitAllRan = false;
92
- }
93
- else {
94
- const currentRoute = normalizeUrl(window.location.href);
95
- const existing = currentRoute ? findCardByNormalizedUrl(currentRoute) : undefined;
96
- if (existing) {
97
- focusCard(existing.id);
98
- }
99
- else {
100
- const added = addCanvasCard(window.location.href, document.title);
101
- focusCard(added.id);
102
- fitAllRan = false;
103
- }
104
- }
93
+ if (next === "canvas")
94
+ resetCameraToActiveCard(viewport);
95
+ presentation = next;
96
+ presentationTransitioning = !window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
97
+ if (presentationTransitionTimer !== null)
98
+ window.clearTimeout(presentationTransitionTimer);
99
+ presentationTransitionTimer = presentationTransitioning
100
+ ? window.setTimeout(() => {
101
+ presentationTransitionTimer = null;
102
+ presentationTransitioning = false;
103
+ notify();
104
+ }, 220)
105
+ : null;
105
106
  notify();
106
107
  }
107
- export function exitCanvas() {
108
- if (mode === "inspect")
108
+ function resetCameraToActiveCard(viewport) {
109
+ const activeCardId = selectedCardId ?? focusedCardId;
110
+ const activeCard = cards.find((card) => card.id === activeCardId) ?? cards[0];
111
+ if (!activeCard) {
112
+ cachedBoardCamera = { ...DEFAULT_CAMERA };
109
113
  return;
110
- mode = "inspect";
111
- notify();
112
- }
113
- /** Return to inspect mode on a canvas card's route. */
114
- export function exitCanvasToCard(card) {
115
- exitCanvas();
116
- resetFitAllFlag();
117
- if (card.url !== window.location.href) {
118
- window.location.href = card.url;
119
114
  }
115
+ const zoom = 0.9;
116
+ const view = viewport ?? defaultViewportSize();
117
+ cachedBoardCamera = {
118
+ x: view.width / 2 - (activeCard.x + activeCard.width / 2) * zoom,
119
+ y: view.height / 2 - (activeCard.y + activeCard.height / 2) * zoom,
120
+ zoom,
121
+ };
120
122
  }
121
123
  /** Adds one card while retaining the existing placement policy. */
122
124
  export function addCanvasCard(url, title) {
@@ -189,15 +191,17 @@ export function removeCanvasComparisonGroup(id) {
189
191
  const groupCardIds = new Set(group.cardIds);
190
192
  cards = cards.filter((card) => !groupCardIds.has(card.id));
191
193
  comparisonGroups = comparisonGroups.filter((candidate) => candidate.id !== id);
192
- if (groupCardIds.has(selectedCardId ?? ""))
193
- selectedCardId = null;
194
- if (groupCardIds.has(focusedCardId ?? ""))
195
- focusedCardId = null;
196
- if (cards.length === 0 && mode === "canvas")
197
- mode = "inspect";
194
+ selectFallbackAfterRemoval(groupCardIds);
198
195
  notify();
199
196
  return group;
200
197
  }
198
+ function selectFallbackAfterRemoval(removedIds) {
199
+ const fallbackId = cards[0]?.id ?? null;
200
+ if (removedIds.has(selectedCardId ?? ""))
201
+ selectedCardId = fallbackId;
202
+ if (removedIds.has(focusedCardId ?? ""))
203
+ focusedCardId = fallbackId;
204
+ }
201
205
  /** Clears group metadata while leaving ordinary cards intact. */
202
206
  export function clearCanvasComparisonGroups() {
203
207
  if (comparisonGroups.length === 0 && !cards.some((card) => card.comparisonGroupId))
@@ -223,13 +227,10 @@ function updateComparisonGroupRoutes(groupId, cardId, patchGroup) {
223
227
  }
224
228
  export function removeCanvasCard(id) {
225
229
  const removed = cards.find((card) => card.id === id);
230
+ if (!removed)
231
+ return;
226
232
  cards = cards.filter((c) => c.id !== id);
227
- if (selectedCardId === id) {
228
- selectedCardId = null;
229
- }
230
- if (focusedCardId === id) {
231
- focusedCardId = null;
232
- }
233
+ selectFallbackAfterRemoval(new Set([id]));
233
234
  if (removed?.comparisonGroupId) {
234
235
  updateComparisonGroupRoutes(removed.comparisonGroupId, id, (group, routeIndex) => ({
235
236
  ...group,
@@ -238,11 +239,62 @@ export function removeCanvasCard(id) {
238
239
  }));
239
240
  comparisonGroups = comparisonGroups.filter((group) => group.cardIds.length > 0);
240
241
  }
241
- if (cards.length === 0 && mode === "canvas") {
242
- mode = "inspect";
243
- }
244
242
  notify();
245
243
  }
244
+ /**
245
+ * Opens one editor target in the iframe workspace without replacing restored
246
+ * cards or their dimensions.
247
+ */
248
+ export function activateIframeWorkspace(url, viewport) {
249
+ const normalized = normalizeUrl(url);
250
+ if (!normalized || normalized.origin !== window.location.origin)
251
+ return null;
252
+ const targetHref = new URL(url).href;
253
+ mode = "canvas";
254
+ const focused = cards.find((card) => card.id === focusedCardId);
255
+ const focusedUrl = focused ? normalizeUrl(focused.url) : null;
256
+ const existing = focused && focusedUrl
257
+ && normalizedUrlKey(focusedUrl) === normalizedUrlKey(normalized)
258
+ ? focused
259
+ : cards.find((card) => card.url === targetHref) ?? findCardByNormalizedUrl(normalized);
260
+ if (existing) {
261
+ let activated = existing;
262
+ if (existing.url !== targetHref) {
263
+ activated = { ...existing, url: targetHref, navigationUrl: targetHref };
264
+ cards = cards.map((card) => card.id === existing.id ? activated : card);
265
+ }
266
+ focusedCardId = existing.id;
267
+ selectedCardId = existing.id;
268
+ notify();
269
+ return activated;
270
+ }
271
+ if (cards.length === 0) {
272
+ const width = Math.max(200, viewport.width - PRIMARY_CARD_INSET * 2);
273
+ const height = Math.max(150, viewport.height - PRIMARY_CARD_INSET * 2);
274
+ const card = {
275
+ id: `card-${++cardIdCounter}`,
276
+ url: targetHref,
277
+ title: null,
278
+ x: 0,
279
+ y: 0,
280
+ width,
281
+ height,
282
+ };
283
+ cards = [card];
284
+ lastUsedCardSize = { width, height };
285
+ focusedCardId = card.id;
286
+ selectedCardId = card.id;
287
+ cachedBoardCamera = { x: PRIMARY_CARD_INSET, y: PRIMARY_CARD_INSET, zoom: 1 };
288
+ fitAllRan = true;
289
+ notify();
290
+ return card;
291
+ }
292
+ const card = addCanvasCard(targetHref);
293
+ focusedCardId = card.id;
294
+ selectedCardId = card.id;
295
+ focusCanvasCards([card.id], viewport);
296
+ return card;
297
+ }
246
298
  export function updateCardTitle(id, title) {
247
299
  const card = cards.find((candidate) => candidate.id === id);
248
300
  cards = cards.map((c) => (c.id === id ? { ...c, title } : c));
@@ -285,8 +337,10 @@ export function duplicateCard(sourceId) {
285
337
  notify();
286
338
  return card;
287
339
  }
288
- export function resizeCard(id, width, height) {
289
- cards = cards.map((c) => (c.id === id ? { ...c, width, height } : c));
340
+ export function resizeCard(id, width, height, position) {
341
+ cards = cards.map((c) => (c.id === id
342
+ ? { ...c, width, height, ...(position ? { x: position.x, y: position.y } : {}) }
343
+ : c));
290
344
  lastUsedCardSize = { width, height };
291
345
  notify();
292
346
  }
@@ -386,6 +440,7 @@ export function getFocusedCardId() {
386
440
  return focusedCardId;
387
441
  }
388
442
  export function selectCard(id) {
443
+ focusedCardId = id;
389
444
  selectedCardId = id;
390
445
  notify();
391
446
  }
@@ -406,7 +461,7 @@ export function setCardPosition(id, x, y) {
406
461
  cards = cards.map((c) => (c.id === id ? { ...c, x, y } : c));
407
462
  notify();
408
463
  }
409
- export function hydrateCanvasStore(newMode, newCards, newCamera, newComparisonGroups = []) {
464
+ export function hydrateCanvasStore(newMode, newCards, newCamera, newComparisonGroups = [], restoredFocusedCardId = null) {
410
465
  mode = newMode;
411
466
  cards = newCards.map((card) => ({ ...card }));
412
467
  comparisonGroups = newComparisonGroups.map((group) => ({
@@ -415,9 +470,15 @@ export function hydrateCanvasStore(newMode, newCards, newCamera, newComparisonGr
415
470
  cardIds: [...group.cardIds],
416
471
  routes: group.routes.map((route) => ({ ...route })),
417
472
  }));
418
- focusedCardId = null;
473
+ focusedCardId = newCards.some((card) => card.id === restoredFocusedCardId)
474
+ ? restoredFocusedCardId
475
+ : null;
419
476
  selectedCardId = null;
420
477
  cachedBoardCamera = { ...newCamera };
478
+ const lastCard = newCards.at(-1);
479
+ lastUsedCardSize = lastCard
480
+ ? { width: lastCard.width, height: lastCard.height }
481
+ : null;
421
482
  if (newCards.length > 0) {
422
483
  let maxNum = 0;
423
484
  for (const c of newCards) {
@@ -437,6 +498,12 @@ export { subscribe, getMode, getCards, getComparisonGroups, getMode as getCanvas
437
498
  export function useCanvasMode() {
438
499
  return useSyncExternalStore(subscribe, getMode, getMode);
439
500
  }
501
+ export function useCanvasPresentation() {
502
+ return useSyncExternalStore(subscribe, getPresentation, getPresentation);
503
+ }
504
+ export function useCanvasPresentationTransitioning() {
505
+ return useSyncExternalStore(subscribe, getPresentationTransitioning, getPresentationTransitioning);
506
+ }
440
507
  export function useCanvasCards() {
441
508
  return useSyncExternalStore(subscribe, getCards, getCards);
442
509
  }
@@ -1,6 +1,8 @@
1
1
  import type { ComponentOverride } from "../componentSemantics/types.js";
2
2
  import type { RenderedInstanceOverride } from "../changes/editModel.js";
3
- export declare const PROTOCOL_VERSION = 15;
3
+ import type { NudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
4
+ import type { SpacingDescriptor } from "../overlay/spacingGestures.js";
5
+ export declare const PROTOCOL_VERSION = 19;
4
6
  export interface FrameMessage {
5
7
  type: string;
6
8
  protocolVersion: number;
@@ -31,6 +33,11 @@ export interface FrameReadyMessage extends RendererMessage {
31
33
  type: "frame-ready";
32
34
  url: string;
33
35
  title: string;
36
+ runtime: NudgeUiRuntimeConfig;
37
+ }
38
+ export interface FrameRuntimeMessage extends RendererMessage {
39
+ type: "frame-runtime";
40
+ runtime: NudgeUiRuntimeConfig;
34
41
  }
35
42
  export interface FrameMetadataMessage extends RendererMessage {
36
43
  type: "frame-metadata";
@@ -111,6 +118,13 @@ export interface ElementHoverMessage extends RendererMessage {
111
118
  bottom: number;
112
119
  left: number;
113
120
  } | null;
121
+ /** Last pointer position in the renderer viewport, used for spacing guides. */
122
+ point?: {
123
+ x: number;
124
+ y: number;
125
+ } | null;
126
+ /** The padding or layout gap under the pointer, when one is draggable. */
127
+ spacing?: SpacingDescriptor | null;
114
128
  }
115
129
  /** Modifier state stays inside one renderer frame; the parent never infers it
116
130
  * from its own keyboard events because focus lives in the iframe. */
@@ -131,6 +145,20 @@ export interface ElementClickMessage extends RendererMessage {
131
145
  /** True for a Shift-click that toggles this target in the selection group. */
132
146
  additive?: boolean;
133
147
  }
148
+ /** Requests controller-owned inline text editing for an element in this frame. */
149
+ export interface InlineTextIntentMessage extends RendererMessage {
150
+ type: "inline-text-intent";
151
+ intent: "pointer-down" | "double-click";
152
+ cid: string;
153
+ src: string;
154
+ elementId: string;
155
+ point: {
156
+ x: number;
157
+ y: number;
158
+ };
159
+ clickCount?: number;
160
+ emptyProjectionId?: string;
161
+ }
134
162
  /** Renderer request to clear the controller-owned element selection. */
135
163
  export interface ElementDeselectMessage extends RendererMessage {
136
164
  type: "element-deselect";
@@ -144,6 +172,13 @@ export interface ElementDragStartMessage extends RendererMessage {
144
172
  x: number;
145
173
  y: number;
146
174
  };
175
+ /** Original pointer position; the current point may already be outside the affordance. */
176
+ startPoint?: {
177
+ x: number;
178
+ y: number;
179
+ };
180
+ /** Spacing affordance captured at pointer-down, if this is a spacing drag. */
181
+ spacing?: SpacingDescriptor | null;
147
182
  }
148
183
  export interface ElementDragMoveMessage extends RendererMessage {
149
184
  type: "element-drag-move";
@@ -158,6 +193,8 @@ export interface ElementDragEndMessage extends RendererMessage {
158
193
  x: number;
159
194
  y: number;
160
195
  };
196
+ /** True when the renderer lost the gesture before the pointer was released. */
197
+ cancelled?: boolean;
161
198
  }
162
199
  export interface ElementDeleteMessage extends RendererMessage {
163
200
  type: "element-delete";
@@ -177,9 +214,21 @@ export interface HistoryRequestMessage extends RendererMessage {
177
214
  type: "history-request";
178
215
  action: "undo" | "redo";
179
216
  }
180
- export interface ExternalNavigationMessage extends RendererMessage {
181
- type: "external-navigation";
182
- url: string;
217
+ /** Requests the parent-owned inspector visibility toggle from frame focus. */
218
+ export interface InspectorToggleRequestMessage extends RendererMessage {
219
+ type: "inspector-toggle-request";
220
+ }
221
+ /** Requests that the parent-owned inspector be open without toggling it closed. */
222
+ export interface InspectorOpenRequestMessage extends RendererMessage {
223
+ type: "inspector-open-request";
224
+ }
225
+ export type KeyboardShortcutCode = "Backslash" | "KeyV" | "KeyH" | "KeyP" | "KeyS";
226
+ export type KeyboardShortcutPhase = "keydown" | "keyup";
227
+ /** Forwards controller-owned keyboard shortcuts from an iframe document. */
228
+ export interface KeyboardShortcutMessage extends RendererMessage {
229
+ type: "keyboard-shortcut";
230
+ phase: KeyboardShortcutPhase;
231
+ code: KeyboardShortcutCode;
183
232
  }
184
233
  export interface PanStartMessage extends RendererMessage {
185
234
  type: "pan-start";
@@ -203,6 +252,16 @@ export interface PanModifierMessage extends RendererMessage {
203
252
  type: "pan-modifier";
204
253
  spaceHeld: boolean;
205
254
  }
255
+ /** Keeps iframe measurement state synchronized when focus is in the parent. */
256
+ export interface MeasureModifierMessage extends RendererMessage {
257
+ type: "measure-modifier";
258
+ altKey: boolean;
259
+ }
260
+ /** Enables board-only pan and zoom interception inside a mounted preview. */
261
+ export interface BoardGestureStateMessage extends RendererMessage {
262
+ type: "board-gesture-state";
263
+ enabled: boolean;
264
+ }
206
265
  /** Proxies modified wheel gestures that originate inside an iframe card. */
207
266
  export interface ZoomMessage extends RendererMessage {
208
267
  type: "zoom";
@@ -212,13 +271,18 @@ export interface ZoomMessage extends RendererMessage {
212
271
  y: number;
213
272
  };
214
273
  }
215
- export type FrameProtocolMessage = ParentReadyMessage | RendererHelloMessage | FrameReadyMessage | FrameMetadataMessage | FrameLoadError | ReplaceStylesMessage | ProjectionAppliedMessage | StructuralProjectionReportMessage | RenderedInstanceProjectionReportMessage | TextProjectionReportMessage | NavigationIntentMessage | ElementHoverMessage | ElementMeasureStateMessage | ElementClickMessage | ElementDeselectMessage | ElementDragStartMessage | ElementDragMoveMessage | ElementDragEndMessage | ElementDeleteMessage | ElementNudgeMessage | HistoryRequestMessage | ExternalNavigationMessage | PanStartMessage | PanMoveMessage | PanEndMessage | PanModifierMessage | ZoomMessage;
274
+ export type FrameProtocolMessage = ParentReadyMessage | RendererHelloMessage | FrameReadyMessage | FrameRuntimeMessage | FrameMetadataMessage | FrameLoadError | ReplaceStylesMessage | ProjectionAppliedMessage | StructuralProjectionReportMessage | RenderedInstanceProjectionReportMessage | TextProjectionReportMessage | NavigationIntentMessage | ElementHoverMessage | ElementMeasureStateMessage | ElementClickMessage | InlineTextIntentMessage | ElementDeselectMessage | ElementDragStartMessage | ElementDragMoveMessage | ElementDragEndMessage | ElementDeleteMessage | ElementNudgeMessage | HistoryRequestMessage | InspectorToggleRequestMessage | InspectorOpenRequestMessage | KeyboardShortcutMessage | PanStartMessage | PanMoveMessage | PanEndMessage | PanModifierMessage | MeasureModifierMessage | BoardGestureStateMessage | ZoomMessage;
216
275
  export declare function setRendererIdentity(identity: FrameIdentity): void;
217
276
  export declare function clearRendererIdentity(expected?: FrameIdentity): void;
218
277
  export declare function getRendererIdentity(): FrameIdentity | null;
219
278
  export declare function isRendererMessageFor(value: unknown, identity: FrameIdentity): value is RendererMessage;
279
+ export declare function isKeyboardShortcutCode(value: unknown): value is KeyboardShortcutCode;
280
+ /** Strict JSON-only schema for renderer-originated global keyboard shortcuts. */
281
+ export declare function isKeyboardShortcutMessage(value: unknown, identity: FrameIdentity): value is KeyboardShortcutMessage;
220
282
  /** Strict JSON-only schema for selecting an element from a renderer frame. */
221
283
  export declare function isElementClickMessage(value: unknown, identity: FrameIdentity): value is ElementClickMessage;
284
+ /** Strict JSON-only schema for starting controller-owned inline text editing. */
285
+ export declare function isInlineTextIntentMessage(value: unknown, identity: FrameIdentity): value is InlineTextIntentMessage;
222
286
  /** Strict JSON-only schema for renderer diagnostics before the parent records them. */
223
287
  export declare function isStructuralProjectionReportMessage(value: unknown, identity: FrameIdentity): value is StructuralProjectionReportMessage;
224
288
  /** Strict JSON-only schema for renderer CSS-instance diagnostics. */