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.
Files changed (258) hide show
  1. package/README.md +164 -46
  2. package/dist/client.mjs +252 -54
  3. package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
  4. package/dist/css/value-semantics/colorSemantics.js +17 -0
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
  6. package/dist/hosts/astro/clientTransport.js +41 -32
  7. package/dist/hosts/astro/integration.d.ts +2 -2
  8. package/dist/hosts/astro/integration.js +9 -4
  9. package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
  10. package/dist/hosts/astro/markdownIdentity.js +81 -0
  11. package/dist/hosts/environment.d.ts +15 -0
  12. package/dist/hosts/environment.js +16 -0
  13. package/dist/hosts/next/manifest.js +1 -1
  14. package/dist/hosts/next/sidecar.js +12 -2
  15. package/dist/hosts/next/wrapper.d.ts +2 -0
  16. package/dist/hosts/next/wrapper.js +2 -1
  17. package/dist/hosts/projectBridge.d.ts +1 -0
  18. package/dist/hosts/projectBridge.js +1 -0
  19. package/dist/hosts/static/manifest.js +3 -1
  20. package/dist/hosts/static/server.js +4 -0
  21. package/dist/hosts/vite/vite.d.ts +4 -0
  22. package/dist/hosts/vite/vite.js +28 -5
  23. package/dist/inspector/agent/McpConnectionDialog.d.ts +3 -2
  24. package/dist/inspector/agent/McpConnectionDialog.js +23 -22
  25. package/dist/inspector/agent/activity.d.ts +18 -0
  26. package/dist/inspector/agent/activity.js +70 -0
  27. package/dist/inspector/agent/client.d.ts +8 -0
  28. package/dist/inspector/agent/client.js +65 -5
  29. package/dist/inspector/agent/connectionStatus.d.ts +3 -3
  30. package/dist/inspector/agent/connectionStatus.js +9 -2
  31. package/dist/inspector/agent/httpTransport.d.ts +6 -0
  32. package/dist/inspector/agent/httpTransport.js +50 -3
  33. package/dist/inspector/agent/protocol.d.ts +9 -3
  34. package/dist/inspector/agent/protocol.js +1 -1
  35. package/dist/inspector/agent/verification.d.ts +11 -1
  36. package/dist/inspector/agent/verification.js +53 -25
  37. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
  38. package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
  39. package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
  40. package/dist/inspector/canvas/CanvasCard.js +133 -38
  41. package/dist/inspector/canvas/CanvasElementOverlay.js +200 -22
  42. package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
  43. package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
  44. package/dist/inspector/canvas/CanvasToolbar.js +8 -4
  45. package/dist/inspector/canvas/CanvasWorkspace.js +243 -56
  46. package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
  47. package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
  48. package/dist/inspector/canvas/agentPresentation.js +64 -52
  49. package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
  50. package/dist/inspector/canvas/canvasGestures.js +1 -1
  51. package/dist/inspector/canvas/canvasStore.d.ts +46 -82
  52. package/dist/inspector/canvas/canvasStore.js +251 -284
  53. package/dist/inspector/canvas/frameContent.d.ts +21 -0
  54. package/dist/inspector/canvas/frameContent.js +44 -0
  55. package/dist/inspector/canvas/frameProtocol.d.ts +34 -4
  56. package/dist/inspector/canvas/frameProtocol.js +9 -2
  57. package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -1
  58. package/dist/inspector/canvas/keyboardShortcuts.js +5 -3
  59. package/dist/inspector/canvas/layout.d.ts +52 -0
  60. package/dist/inspector/canvas/layout.js +106 -0
  61. package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
  62. package/dist/inspector/canvas/linkEligibility.js +0 -29
  63. package/dist/inspector/canvas/projection.d.ts +15 -5
  64. package/dist/inspector/canvas/projection.js +69 -13
  65. package/dist/inspector/canvas/rendererBootstrap.js +45 -4
  66. package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
  67. package/dist/inspector/canvas/rendererElementSelector.js +102 -45
  68. package/dist/inspector/canvas/rendererStylesheet.js +2 -2
  69. package/dist/inspector/canvas/resizeHandleScale.js +1 -1
  70. package/dist/inspector/canvas/sessionStore.d.ts +11 -26
  71. package/dist/inspector/canvas/sessionStore.js +43 -183
  72. package/dist/inspector/canvas/staleChangeDetector.js +9 -3
  73. package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
  74. package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
  75. package/dist/inspector/canvas/viewStore.d.ts +38 -0
  76. package/dist/inspector/canvas/viewStore.js +104 -0
  77. package/dist/inspector/canvas/wheelPan.d.ts +8 -0
  78. package/dist/inspector/canvas/wheelPan.js +8 -0
  79. package/dist/inspector/changes/changesLog.d.ts +6 -5
  80. package/dist/inspector/changes/changesLog.js +40 -32
  81. package/dist/inspector/changes/draftChanges.d.ts +56 -0
  82. package/dist/inspector/changes/draftChanges.js +300 -0
  83. package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
  84. package/dist/inspector/changes/previewDiagnostics.js +11 -25
  85. package/dist/inspector/clientManifest.d.ts +3 -1
  86. package/dist/inspector/clientManifest.js +1 -0
  87. package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
  88. package/dist/inspector/comments/CommentsOverlay.js +250 -0
  89. package/dist/inspector/comments/element.d.ts +6 -0
  90. package/dist/inspector/comments/element.js +47 -0
  91. package/dist/inspector/comments/store.d.ts +29 -0
  92. package/dist/inspector/comments/store.js +121 -0
  93. package/dist/inspector/comments/verification.d.ts +5 -0
  94. package/dist/inspector/comments/verification.js +52 -0
  95. package/dist/inspector/drafts/model.d.ts +35 -0
  96. package/dist/inspector/drafts/model.js +45 -0
  97. package/dist/inspector/drafts/store.d.ts +28 -0
  98. package/dist/inspector/drafts/store.js +231 -0
  99. package/dist/inspector/index.js +26 -35
  100. package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
  101. package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
  102. package/dist/inspector/iterations/capture.d.ts +2 -0
  103. package/dist/inspector/iterations/capture.js +173 -0
  104. package/dist/inspector/iterations/client.d.ts +7 -0
  105. package/dist/inspector/iterations/client.js +52 -0
  106. package/dist/inspector/iterations/coordinator.d.ts +15 -0
  107. package/dist/inspector/iterations/coordinator.js +130 -0
  108. package/dist/inspector/iterations/prompt.d.ts +3 -0
  109. package/dist/inspector/iterations/prompt.js +26 -0
  110. package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
  111. package/dist/inspector/overlay/DomNavigator.js +46 -0
  112. package/dist/inspector/overlay/InspectorOverlay.js +6 -2
  113. package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
  114. package/dist/inspector/overlay/clickPolicy.js +34 -3
  115. package/dist/inspector/overlay/elementSelector.d.ts +1 -1
  116. package/dist/inspector/overlay/elementSelector.js +18 -7
  117. package/dist/inspector/overlay/interactionStyles.js +3 -1
  118. package/dist/inspector/overlay/spacingGestures.d.ts +15 -3
  119. package/dist/inspector/overlay/spacingGestures.js +88 -13
  120. package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
  121. package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
  122. package/dist/inspector/projection/renderedInstance.d.ts +2 -1
  123. package/dist/inspector/projection/renderedInstance.js +9 -5
  124. package/dist/inspector/projection/structuralProjection.d.ts +2 -1
  125. package/dist/inspector/projection/structuralProjection.js +19 -15
  126. package/dist/inspector/projection/textProjection.d.ts +2 -1
  127. package/dist/inspector/projection/textProjection.js +8 -4
  128. package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
  129. package/dist/inspector/prompt/clipboardHandoff.d.ts +10 -18
  130. package/dist/inspector/prompt/clipboardHandoff.js +92 -143
  131. package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
  132. package/dist/inspector/prompt/generatePrompt.js +17 -16
  133. package/dist/inspector/runtime/runtimeConfig.d.ts +10 -1
  134. package/dist/inspector/runtime/runtimeConfig.js +24 -0
  135. package/dist/inspector/selection/hierarchy.d.ts +6 -0
  136. package/dist/inspector/selection/hierarchy.js +8 -0
  137. package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
  138. package/dist/inspector/settings/SettingsDialog.js +22 -9
  139. package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
  140. package/dist/inspector/shell/AgentStatusButton.js +36 -0
  141. package/dist/inspector/shell/AppShell.js +4 -2
  142. package/dist/inspector/shell/ChangesLog.js +47 -27
  143. package/dist/inspector/shell/CopyPromptButton.js +146 -90
  144. package/dist/inspector/shell/EmptyState.js +9 -8
  145. package/dist/inspector/shell/InspectorShell.js +14 -25
  146. package/dist/inspector/shell/PeekOriginalButton.js +24 -23
  147. package/dist/inspector/shell/originalPreview.js +5 -5
  148. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  149. package/dist/inspector/shell/shortcuts.js +11 -0
  150. package/dist/inspector/sketch/SketchEditor.js +14 -14
  151. package/dist/inspector/sketch/SketchEntryButton.js +1 -1
  152. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
  153. package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
  154. package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
  155. package/dist/inspector/sketch/SketchOverlay.js +7 -7
  156. package/dist/inspector/sketch/SketchWorkspace.js +18 -12
  157. package/dist/inspector/sketch/interaction.d.ts +1 -0
  158. package/dist/inspector/sketch/interaction.js +8 -4
  159. package/dist/inspector/sketch/model.d.ts +5 -0
  160. package/dist/inspector/sketch/model.js +6 -2
  161. package/dist/inspector/sketch/raster.d.ts +1 -2
  162. package/dist/inspector/sketch/raster.js +68 -4
  163. package/dist/inspector/sketch/store.js +48 -23
  164. package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
  165. package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
  166. package/dist/inspector/styleEditors/BorderEditor.js +49 -78
  167. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
  168. package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
  169. package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
  170. package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
  171. package/dist/inspector/styleEditors/ColorPicker.js +10 -4
  172. package/dist/inspector/styleEditors/GapField.js +3 -5
  173. package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
  174. package/dist/inspector/styleEditors/GridChildSection.js +18 -9
  175. package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
  176. package/dist/inspector/styleEditors/GridPicker.js +8 -2
  177. package/dist/inspector/styleEditors/GridSection.js +1 -1
  178. package/dist/inspector/styleEditors/GridValueField.js +19 -14
  179. package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
  180. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
  181. package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
  182. package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
  183. package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
  184. package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
  185. package/dist/inspector/styleEditors/LayoutSection.js +32 -11
  186. package/dist/inspector/styleEditors/SpacingBox.js +16 -9
  187. package/dist/inspector/styleEditors/Typography.js +11 -3
  188. package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
  189. package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
  190. package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
  191. package/dist/inspector/styleEditors/gradientValue.js +114 -0
  192. package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
  193. package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
  194. package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
  195. package/dist/inspector/styleEditors/layoutValue.js +0 -42
  196. package/dist/inspector/styleEditors/nudgeValue.d.ts +4 -4
  197. package/dist/inspector/styleEditors/nudgeValue.js +41 -11
  198. package/dist/inspector/tokens/TokenField.d.ts +7 -0
  199. package/dist/inspector/tokens/TokenField.js +49 -22
  200. package/dist/inspector/tokens/TokenLabel.js +5 -14
  201. package/dist/inspector/tokens/TokensPanel.js +2 -1
  202. package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
  203. package/dist/inspector/tokens/dragPointerLock.js +70 -0
  204. package/dist/inspector/tokens/editActions.d.ts +1 -1
  205. package/dist/inspector/tokens/editActions.js +1 -1
  206. package/dist/inspector/ui/AtRuleContext.js +4 -6
  207. package/dist/inspector/ui/Button.d.ts +6 -2
  208. package/dist/inspector/ui/Button.js +4 -3
  209. package/dist/inspector/ui/ControlSurface.d.ts +2 -2
  210. package/dist/inspector/ui/ControlSurface.js +4 -3
  211. package/dist/inspector/ui/Disclosure.d.ts +22 -0
  212. package/dist/inspector/ui/Disclosure.js +13 -0
  213. package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
  214. package/dist/inspector/ui/InlineStyleWarning.js +13 -0
  215. package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
  216. package/dist/inspector/ui/InspectorPopover.js +4 -5
  217. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  218. package/dist/inspector/ui/PopoverListbox.js +8 -7
  219. package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
  220. package/dist/inspector/ui/SegmentedControl.js +2 -2
  221. package/dist/inspector/ui/Select.d.ts +6 -1
  222. package/dist/inspector/ui/Select.js +7 -11
  223. package/dist/inspector/ui/SideValuesField.js +1 -1
  224. package/dist/inspector/ui/Tooltip.d.ts +26 -0
  225. package/dist/inspector/ui/Tooltip.js +32 -0
  226. package/dist/inspector/ui/classNames.d.ts +2 -0
  227. package/dist/inspector/ui/classNames.js +4 -0
  228. package/dist/inspector/ui/customCursors.d.ts +23 -0
  229. package/dist/inspector/ui/customCursors.js +122 -0
  230. package/dist/inspector/ui/dragCursor.d.ts +6 -0
  231. package/dist/inspector/ui/dragCursor.js +13 -0
  232. package/dist/inspector/ui/portalContainer.d.ts +2 -0
  233. package/dist/inspector/ui/portalContainer.js +6 -0
  234. package/dist/inspector/ui/styles.js +34 -24
  235. package/dist/inspector/workspace/commands.d.ts +9 -0
  236. package/dist/inspector/workspace/commands.js +82 -0
  237. package/dist/inspector/workspace/controller.d.ts +4 -0
  238. package/dist/inspector/workspace/controller.js +66 -0
  239. package/dist/inspector/workspace/handoff.d.ts +42 -0
  240. package/dist/inspector/workspace/handoff.js +115 -0
  241. package/dist/inspector/workspace/timeline.d.ts +30 -0
  242. package/dist/inspector/workspace/timeline.js +108 -0
  243. package/dist/internal/agent-protocol.d.ts +19 -2
  244. package/dist/internal/agent-protocol.js +13 -0
  245. package/dist/nudge-ui.mjs +560 -299
  246. package/dist/project/artifacts.d.ts +13 -0
  247. package/dist/project/artifacts.js +253 -0
  248. package/dist/transport/artifacts.d.ts +8 -0
  249. package/dist/transport/artifacts.js +9 -0
  250. package/dist/transport/editor.d.ts +20 -1
  251. package/dist/transport/editor.js +44 -11
  252. package/dist/transport/routes.d.ts +6 -1
  253. package/dist/transport/routes.js +6 -1
  254. package/package.json +6 -2
  255. package/dist/inspector/changes/workspaceChanges.d.ts +0 -43
  256. package/dist/inspector/changes/workspaceChanges.js +0 -239
  257. package/dist/inspector/shell/DomNavigation.d.ts +0 -8
  258. package/dist/inspector/shell/DomNavigation.js +0 -30
@@ -0,0 +1,35 @@
1
+ import type { DraftContents } from "../changes/draftChanges.js";
2
+ export type DraftTarget = {
3
+ readonly kind: "application";
4
+ readonly route: string;
5
+ } | {
6
+ readonly kind: "html";
7
+ readonly artifactId: string;
8
+ readonly path: string;
9
+ };
10
+ /** A draft's ID is its target key, so every frame showing the same page or iteration edits one draft. */
11
+ export interface Draft {
12
+ readonly id: string;
13
+ readonly target: DraftTarget;
14
+ readonly revision: number;
15
+ readonly contents: DraftContents;
16
+ readonly sketchIds: readonly string[];
17
+ }
18
+ export interface DraftSnapshot {
19
+ readonly projectId: string;
20
+ readonly drafts: readonly Draft[];
21
+ }
22
+ export declare function applicationTarget(url?: string): {
23
+ kind: "application";
24
+ route: string;
25
+ };
26
+ export declare function htmlTarget(artifactId: string): {
27
+ kind: "html";
28
+ artifactId: string;
29
+ path: string;
30
+ };
31
+ /** Maps a captured document URL to the draft that owns it: an iteration preview or an application page. */
32
+ export declare function documentTarget(url: string): DraftTarget;
33
+ export declare function targetKey(target: DraftTarget): string;
34
+ export declare function targetFromKey(key: string): DraftTarget | null;
35
+ export declare function isDraftTarget(value: unknown): value is DraftTarget;
@@ -0,0 +1,45 @@
1
+ import { normalizeUrl, normalizedUrlKey } from "../canvas/normalizeUrl.js";
2
+ const ARTIFACT_PATH = /^\/__nudge_ui__\/artifacts\/([a-f0-9-]{36})\//i;
3
+ export function applicationTarget(url = window.location.href) {
4
+ const page = new URL(url, window.location.href);
5
+ page.searchParams.delete("nudge-ui");
6
+ page.searchParams.sort();
7
+ const normalized = normalizeUrl(page.href);
8
+ return { kind: "application", route: normalizedUrlKey(normalized) };
9
+ }
10
+ export function htmlTarget(artifactId) {
11
+ return { kind: "html", artifactId, path: `.nudge/artifacts/${artifactId}/document.html` };
12
+ }
13
+ /** Maps a captured document URL to the draft that owns it: an iteration preview or an application page. */
14
+ export function documentTarget(url) {
15
+ const artifactId = ARTIFACT_PATH.exec(new URL(url, window.location.href).pathname)?.[1];
16
+ return artifactId ? htmlTarget(artifactId) : applicationTarget(url);
17
+ }
18
+ export function targetKey(target) {
19
+ return target.kind === "application" ? `application:${target.route}` : `html:${target.artifactId}`;
20
+ }
21
+ export function targetFromKey(key) {
22
+ if (key.startsWith("html:"))
23
+ return htmlTarget(key.slice("html:".length));
24
+ if (!key.startsWith("application:"))
25
+ return null;
26
+ const target = { kind: "application", route: key.slice("application:".length) };
27
+ return isDraftTarget(target) ? target : null;
28
+ }
29
+ export function isDraftTarget(value) {
30
+ if (!value || typeof value !== "object")
31
+ return false;
32
+ const target = value;
33
+ if (target.kind === "application") {
34
+ if (typeof target.route !== "string")
35
+ return false;
36
+ try {
37
+ return applicationTarget(target.route).route === target.route;
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ return target.kind === "html" && typeof target.artifactId === "string"
44
+ && target.path === `.nudge/artifacts/${target.artifactId}/document.html`;
45
+ }
@@ -0,0 +1,28 @@
1
+ import { type FrameContent } from "../canvas/frameContent.js";
2
+ import { type DraftContents } from "../changes/draftChanges.js";
3
+ import type { SketchDocument } from "../sketch/model.js";
4
+ import { type Draft, type DraftSnapshot } from "./model.js";
5
+ export declare function draftIdForContent(content: FrameContent): string;
6
+ export declare function draftIdForCard(cardId: string): string | null;
7
+ export declare function sketchOwner(document: SketchDocument): string | null;
8
+ export declare function getDraft(id: string): Draft | null;
9
+ export declare function getDraftForCard(cardId: string): Draft | null;
10
+ export declare function getDraftContentsForCard(cardId: string): DraftContents | null;
11
+ export declare function persistDrafts(): void;
12
+ /** Replaces every in-memory draft with the stored drafts, so a tab that takes over never writes stale edits. */
13
+ export declare function loadDrafts(nextProjectId: string, options?: {
14
+ persistent?: boolean;
15
+ }): void;
16
+ export declare function activateDraftForCard(cardId: string): void;
17
+ export declare function activateDraft(draftId: string, preferredCardId?: string): void;
18
+ export declare function clearSavedIterationDraft(cardId: string, expectedRevision?: number): boolean;
19
+ /** Returns the first preferred frame that exists, then the first frame. */
20
+ export declare function getEditableCardId(...preferredCardIds: readonly (string | null | undefined)[]): string | null;
21
+ export declare function assignSketchToDraft(draftId: string, sketchId: string): void;
22
+ export declare function sketchBelongsToCard(document: SketchDocument, cardId: string | null): boolean;
23
+ export declare function subscribeDrafts(listener: () => void): () => void;
24
+ export declare function getDraftsSnapshot(): DraftSnapshot;
25
+ export declare function useDrafts(): DraftSnapshot;
26
+ /** Discards stored and in-memory drafts for the current project. */
27
+ export declare function clearDrafts(): void;
28
+ export declare function resetDrafts(): void;
@@ -0,0 +1,231 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { getCanvasCards, selectCard, focusCanvasCards, activateIframeWorkspace } from "../canvas/canvasStore.js";
3
+ import { contentEditTarget } from "../canvas/frameContent.js";
4
+ import { deserializeChange, isSerializableChange, serializeChange, } from "../changes/codecs.js";
5
+ import { getActiveDraftChanges, getDraftChanges, restoreDraftChanges, subscribeDraftChanges, setActiveDraft, clearDraftChanges, listDraftIds, resetDraftChanges, } from "../changes/draftChanges.js";
6
+ import { refreshDraftPreview } from "../changes/changesLog.js";
7
+ import { isStructuralChange } from "../projection/structuralProjection.js";
8
+ import { canWriteWorkspace } from "../canvas/workspaceLease.js";
9
+ import { getSketchStoreSnapshot, subscribeSketchStore } from "../sketch/store.js";
10
+ import { documentTarget, isDraftTarget, targetFromKey, targetKey } from "./model.js";
11
+ let projectId = "";
12
+ let persistent = true;
13
+ /** Overrides the capture-URL owner for sketches drawn while another draft was active. */
14
+ const sketchOwners = new Map();
15
+ const contentDrafts = new WeakMap();
16
+ const listeners = new Set();
17
+ let snapshot = null;
18
+ function publish() {
19
+ snapshot = null;
20
+ for (const listener of listeners)
21
+ listener();
22
+ }
23
+ subscribeDraftChanges(publish);
24
+ subscribeSketchStore(publish);
25
+ function storageKey() { return `nudge-ui-drafts:${projectId}:v2`; }
26
+ export function draftIdForContent(content) {
27
+ let id = contentDrafts.get(content);
28
+ if (id === undefined) {
29
+ id = targetKey(contentEditTarget(content));
30
+ contentDrafts.set(content, id);
31
+ }
32
+ return id;
33
+ }
34
+ export function draftIdForCard(cardId) {
35
+ const card = getCanvasCards().find((candidate) => candidate.id === cardId);
36
+ return card ? draftIdForContent(card.content) : null;
37
+ }
38
+ export function sketchOwner(document) {
39
+ const owner = sketchOwners.get(document.id);
40
+ if (owner)
41
+ return owner;
42
+ try {
43
+ return targetKey(documentTarget(document.capture.url));
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ function readDraft(id, target) {
50
+ const workspace = getDraftChanges(id);
51
+ return {
52
+ id,
53
+ target,
54
+ revision: workspace.revision,
55
+ contents: { changes: workspace.changes, structuralChanges: workspace.structuralChanges },
56
+ sketchIds: getSketchStoreSnapshot().documents.filter((document) => sketchOwner(document) === id).map((document) => document.id),
57
+ };
58
+ }
59
+ export function getDraft(id) {
60
+ const target = targetFromKey(id);
61
+ return target ? readDraft(id, target) : null;
62
+ }
63
+ export function getDraftForCard(cardId) {
64
+ const id = draftIdForCard(cardId);
65
+ return id ? getDraft(id) : null;
66
+ }
67
+ export function getDraftContentsForCard(cardId) {
68
+ const draft = getDraftForCard(cardId);
69
+ return draft ? { changes: draft.contents.changes, structuralChanges: draft.contents.structuralChanges } : null;
70
+ }
71
+ export function persistDrafts() {
72
+ if (!projectId || !persistent || !canWriteWorkspace())
73
+ return;
74
+ const sketches = getSketchStoreSnapshot();
75
+ const liveSketches = new Set(sketches.documents.map((document) => document.id));
76
+ const value = {
77
+ schemaVersion: 2, projectId,
78
+ drafts: listDraftIds().flatMap((id) => {
79
+ const target = targetFromKey(id);
80
+ const workspace = getDraftChanges(id);
81
+ if (!target || workspace.changes.length + workspace.structuralChanges.length === 0)
82
+ return [];
83
+ return [{
84
+ target,
85
+ revision: workspace.revision,
86
+ contents: {
87
+ changes: workspace.changes.flatMap((change) => {
88
+ const serialized = serializeChange(change);
89
+ return serialized ? [serialized] : [];
90
+ }),
91
+ structuralChanges: workspace.structuralChanges,
92
+ },
93
+ }];
94
+ }),
95
+ sketchOwners: Object.fromEntries([...sketchOwners].filter(([id]) => !sketches.ready || liveSketches.has(id))),
96
+ };
97
+ try {
98
+ localStorage.setItem(storageKey(), JSON.stringify(value));
99
+ }
100
+ catch {
101
+ return;
102
+ }
103
+ }
104
+ function validPersisted(value) {
105
+ if (!value || typeof value !== "object")
106
+ return false;
107
+ const candidate = value;
108
+ if (candidate.schemaVersion !== 2 || candidate.projectId !== projectId || !Array.isArray(candidate.drafts))
109
+ return false;
110
+ const targets = new Set();
111
+ for (const draft of candidate.drafts) {
112
+ if (!draft || !isDraftTarget(draft.target) || targets.has(targetKey(draft.target))
113
+ || !Number.isSafeInteger(draft.revision) || draft.revision < 1
114
+ || !draft.contents || !Array.isArray(draft.contents.changes) || !draft.contents.changes.every(isSerializableChange)
115
+ || !Array.isArray(draft.contents.structuralChanges) || !draft.contents.structuralChanges.every(isStructuralChange))
116
+ return false;
117
+ const textIds = draft.contents.changes.filter((change) => change.kind === "text-content").map((change) => "id" in change ? change.id : "");
118
+ if (new Set(textIds).size !== textIds.length)
119
+ return false;
120
+ targets.add(targetKey(draft.target));
121
+ }
122
+ return !!candidate.sketchOwners && typeof candidate.sketchOwners === "object" && !Array.isArray(candidate.sketchOwners)
123
+ && Object.values(candidate.sketchOwners).every((id) => typeof id === "string" && targetFromKey(id) !== null);
124
+ }
125
+ /** Replaces every in-memory draft with the stored drafts, so a tab that takes over never writes stale edits. */
126
+ export function loadDrafts(nextProjectId, options = {}) {
127
+ projectId = nextProjectId;
128
+ persistent = options.persistent ?? true;
129
+ sketchOwners.clear();
130
+ resetDraftChanges();
131
+ try {
132
+ const raw = persistent ? localStorage.getItem(storageKey()) : null;
133
+ const stored = raw ? JSON.parse(raw) : null;
134
+ if (validPersisted(stored)) {
135
+ for (const draft of stored.drafts) {
136
+ restoreDraftChanges(targetKey(draft.target), { changes: draft.contents.changes.map(deserializeChange), structuralChanges: draft.contents.structuralChanges }, draft.revision);
137
+ }
138
+ for (const [sketchId, draftId] of Object.entries(stored.sketchOwners))
139
+ sketchOwners.set(sketchId, draftId);
140
+ }
141
+ }
142
+ catch {
143
+ resetDraftChanges();
144
+ sketchOwners.clear();
145
+ }
146
+ publish();
147
+ refreshDraftPreview();
148
+ }
149
+ export function activateDraftForCard(cardId) {
150
+ const draftId = draftIdForCard(cardId);
151
+ if (draftId)
152
+ activateDraft(draftId, cardId);
153
+ }
154
+ export function activateDraft(draftId, preferredCardId) {
155
+ const previous = getActiveDraftChanges().draftId;
156
+ setActiveDraft(draftId, () => revealDraft(draftId, preferredCardId));
157
+ if (previous !== draftId)
158
+ refreshDraftPreview();
159
+ }
160
+ function revealDraft(draftId, preferredCardId) {
161
+ const cards = getCanvasCards();
162
+ let frame = cards.find((card) => card.id === preferredCardId && draftIdForContent(card.content) === draftId)
163
+ ?? cards.find((card) => draftIdForContent(card.content) === draftId);
164
+ const target = targetFromKey(draftId);
165
+ if (!frame && target?.kind === "application") {
166
+ const original = cards.find((card) => card.id === preferredCardId && card.content.kind === "route");
167
+ if (original) {
168
+ selectCard(original.id);
169
+ frame = activateIframeWorkspace(target.route, { width: window.innerWidth, height: window.innerHeight }, { replaceActiveCard: true }) ?? undefined;
170
+ }
171
+ }
172
+ if (!frame)
173
+ return;
174
+ activateDraftForCard(frame.id);
175
+ selectCard(frame.id);
176
+ focusCanvasCards([frame.id]);
177
+ }
178
+ export function clearSavedIterationDraft(cardId, expectedRevision) {
179
+ const draftId = draftIdForCard(cardId);
180
+ return !!draftId && clearDraftChanges(draftId, expectedRevision);
181
+ }
182
+ /** Returns the first preferred frame that exists, then the first frame. */
183
+ export function getEditableCardId(...preferredCardIds) {
184
+ const cards = getCanvasCards();
185
+ return preferredCardIds.find((id) => !!id && cards.some((card) => card.id === id)) ?? cards[0]?.id ?? null;
186
+ }
187
+ export function assignSketchToDraft(draftId, sketchId) {
188
+ if (sketchOwners.get(sketchId) === draftId || !targetFromKey(draftId))
189
+ return;
190
+ sketchOwners.set(sketchId, draftId);
191
+ publish();
192
+ }
193
+ export function sketchBelongsToCard(document, cardId) {
194
+ if (!projectId || !cardId)
195
+ return true;
196
+ const draftId = draftIdForCard(cardId);
197
+ return !!draftId && sketchOwner(document) === draftId;
198
+ }
199
+ export function subscribeDrafts(listener) {
200
+ listeners.add(listener);
201
+ return () => listeners.delete(listener);
202
+ }
203
+ export function getDraftsSnapshot() {
204
+ if (!snapshot) {
205
+ const ids = new Set([...listDraftIds(), ...getSketchStoreSnapshot().documents.flatMap((document) => sketchOwner(document) ?? [])]);
206
+ snapshot = { projectId, drafts: [...ids].flatMap((id) => getDraft(id) ?? []) };
207
+ }
208
+ return snapshot;
209
+ }
210
+ export function useDrafts() { return useSyncExternalStore(subscribeDrafts, getDraftsSnapshot, getDraftsSnapshot); }
211
+ /** Discards stored and in-memory drafts for the current project. */
212
+ export function clearDrafts() {
213
+ if (projectId && persistent && canWriteWorkspace()) {
214
+ try {
215
+ localStorage.removeItem(storageKey());
216
+ }
217
+ catch {
218
+ return;
219
+ }
220
+ }
221
+ sketchOwners.clear();
222
+ resetDraftChanges();
223
+ publish();
224
+ }
225
+ export function resetDrafts() {
226
+ projectId = "";
227
+ persistent = true;
228
+ sketchOwners.clear();
229
+ resetDraftChanges();
230
+ publish();
231
+ }
@@ -1,3 +1,4 @@
1
+ import { startWorkspaceController } from "./workspace/controller.js";
1
2
  import { createElement } from "react";
2
3
  import { createRoot } from "react-dom/client";
3
4
  import { InspectorShell, toggleInspector, setInspectorOpen } from "./shell/InspectorShell.js";
@@ -8,20 +9,19 @@ import { isCanvasRenderer } from "./canvas/roleDetection.js";
8
9
  import { isKeyboardShortcutMessage } from "./canvas/frameProtocol.js";
9
10
  import { subscribeCanvasRendererMessages } from "./canvas/rendererMessageRouter.js";
10
11
  import { bootstrapRenderer } from "./canvas/rendererBootstrap.js";
11
- import { hydrateSession, enableAutoSave, scheduleAutoSave, scheduleCanvasSave, setRestoreCount, } from "./canvas/sessionStore.js";
12
- import { subscribeChanges, getChangesList } from "./changes/changesLog.js";
13
- import { subscribe as subscribeCanvas } from "./canvas/canvasStore.js";
14
- import { acquireLease, enableWriteGuard, hasWriteLease, releaseLease, subscribeOwnership, } from "./canvas/workspaceLease.js";
12
+ import { setRestoreCount, } from "./canvas/sessionStore.js";
13
+ import { getChangesList } from "./changes/changesLog.js";
14
+ import { acquireLease, enableWriteGuard, hasWriteLease, subscribeOwnership, } from "./canvas/workspaceLease.js";
15
15
  import { cancelStaleDetection, startStaleDetection } from "./canvas/staleChangeDetector.js";
16
16
  import { LockedWorkspaceNotice } from "./canvas/LockedWorkspaceNotice.js";
17
17
  import { AppShell } from "./shell/AppShell.js";
18
18
  import { installInspectionBridge } from "./inspection/bridge.js";
19
- import { releaseDocumentProjection, subscribeStructuralChanges } from "./projection/structuralProjection.js";
19
+ import { releaseDocumentProjection, getStructuralChanges } from "./projection/structuralProjection.js";
20
20
  import { cancelInlineTextEdit } from "./inline-text/inlineTextEditor.js";
21
21
  import { configureNudgeUiRuntime, getNudgeUiRuntimeConfig, isDemoRuntime } from "./runtime/runtimeConfig.js";
22
- import { setCanvasMode } from "./canvas/canvasStore.js";
22
+ import { setCanvasMode } from "./canvas/viewStore.js";
23
23
  import { isNudgeUiDev, setNudgeUiHostDevFlag } from "./runtime/devFlag.js";
24
- import { clearClipboardHandoff, subscribeClipboardHandoff, } from "./prompt/clipboardHandoff.js";
24
+ import { clearClipboardHandoff, } from "./prompt/clipboardHandoff.js";
25
25
  import { createWorkspace, InspectorSessionProvider, } from "./session/index.js";
26
26
  import { clearSketchClipboardHandoff } from "./sketch/handoff.js";
27
27
  import { cancelSketchInteraction, isSketchInteractionActive } from "./sketch/interaction.js";
@@ -34,8 +34,7 @@ let lockedRoot = null;
34
34
  const workspace = createWorkspace();
35
35
  let inspectorSession = null;
36
36
  let rendererBootstrapHandle = null;
37
- let beforeUnloadAttached = false;
38
- let persistenceSubscribed = false;
37
+ let workspaceController = null;
39
38
  let unsubscribeOwnership = null;
40
39
  let removeInspectionBridge = null;
41
40
  let keyboardPeekHeld = false;
@@ -105,6 +104,8 @@ export function bootstrapNudgeUi(inspectorHost) {
105
104
  // The public demo shares the iframe editor while keeping the write lease,
106
105
  // persistence, and agent bridge disabled.
107
106
  setCanvasMode("canvas");
107
+ workspaceController?.dispose();
108
+ workspaceController = startWorkspaceController();
108
109
  mountInspector(inspectorHost);
109
110
  setInspectorOpen(false);
110
111
  return;
@@ -142,12 +143,17 @@ function startController(inspectorHost) {
142
143
  void initializeSketchStore(getNudgeUiRuntimeConfig().projectId).catch(() => undefined);
143
144
  unsubscribeOwnership?.();
144
145
  unsubscribeOwnership = subscribeOwnership((hasLease) => {
145
- if (!hasLease)
146
+ if (!hasLease) {
147
+ workspaceController?.dispose();
148
+ workspaceController = null;
146
149
  mountLockedNotice(inspectorHost);
150
+ }
147
151
  });
148
- const result = hydrateSession();
152
+ workspaceController?.dispose();
153
+ const result = startWorkspaceController();
154
+ workspaceController = result;
149
155
  if (result.restored) {
150
- setRestoreCount(result.changeCount);
156
+ setRestoreCount(getChangesList().length + getStructuralChanges().length);
151
157
  const restored = getChangesList();
152
158
  if (restored.length > 0) {
153
159
  startStaleDetection(restored);
@@ -159,21 +165,6 @@ function startController(inspectorHost) {
159
165
  setCanvasMode("canvas");
160
166
  }
161
167
  mountInspector(inspectorHost);
162
- enableAutoSave();
163
- if (!beforeUnloadAttached) {
164
- // enableAutoSave registers its flush listener first. Release the lease only
165
- // after the pending session write has had a chance to pass the ownership
166
- // gate during beforeunload.
167
- window.addEventListener("beforeunload", releaseLease);
168
- beforeUnloadAttached = true;
169
- }
170
- if (!persistenceSubscribed) {
171
- subscribeChanges(() => scheduleAutoSave());
172
- subscribeStructuralChanges(() => scheduleAutoSave());
173
- subscribeClipboardHandoff(() => scheduleAutoSave());
174
- subscribeCanvas(() => scheduleCanvasSave());
175
- persistenceSubscribed = true;
176
- }
177
168
  }
178
169
  function mountLockedNotice(host) {
179
170
  if (reactRoot) {
@@ -208,14 +199,12 @@ export function mountInspector(host) {
208
199
  const demo = isDemoRuntime();
209
200
  if (!demo && !hasWriteLease())
210
201
  return;
211
- if (host.dataset.nudgeUiDebug === "true") {
212
- const runtimeConfig = getNudgeUiRuntimeConfig();
213
- if (runtimeConfig.capabilities.domNavigation !== true) {
214
- configureNudgeUiRuntime({
215
- ...runtimeConfig,
216
- capabilities: { ...runtimeConfig.capabilities, domNavigation: true },
217
- });
218
- }
202
+ const runtimeConfig = getNudgeUiRuntimeConfig();
203
+ if (runtimeConfig.capabilities.domNavigation !== true) {
204
+ configureNudgeUiRuntime({
205
+ ...runtimeConfig,
206
+ capabilities: { ...runtimeConfig.capabilities, domNavigation: true },
207
+ });
219
208
  }
220
209
  if (lockedRoot) {
221
210
  lockedRoot.unmount();
@@ -241,6 +230,8 @@ export function mountInspector(host) {
241
230
  export function unmountInspector() {
242
231
  rendererBootstrapHandle?.teardown();
243
232
  rendererBootstrapHandle = null;
233
+ workspaceController?.dispose();
234
+ workspaceController = null;
244
235
  unsubscribeOwnership?.();
245
236
  unsubscribeOwnership = null;
246
237
  if (lockedRoot) {
@@ -1,3 +1,4 @@
1
+ import { canEditDraft } from "../changes/draftChanges.js";
1
2
  import { useSyncExternalStore } from "react";
2
3
  import { appendChange } from "../changes/changesLog.js";
3
4
  import { registerInlineTextClearHandler } from "./inlineTextLifecycle.js";
@@ -1029,6 +1030,9 @@ export const inlineTextEditor = {
1029
1030
  /** Re-enter an existing canonical rendered-text projection through its empty
1030
1031
  * inspector affordance. This never guesses a new DOM target or source node. */
1031
1032
  export function beginInlineTextEditFromEmptyProjection(marker) {
1033
+ if (!canEditDraft()) {
1034
+ return { kind: "rejected", reason: "editing-active", message: "Wait for the iteration to finish saving before editing text." };
1035
+ }
1032
1036
  if (!isNudgeUiDev()) {
1033
1037
  return {
1034
1038
  kind: "rejected",
@@ -1063,6 +1067,9 @@ export function beginInlineTextEditFromEmptyProjection(marker) {
1063
1067
  }
1064
1068
  }
1065
1069
  export function beginInlineTextEdit(element, point) {
1070
+ if (!canEditDraft()) {
1071
+ return { kind: "rejected", reason: "editing-active", message: "Wait for the iteration to finish saving before editing text." };
1072
+ }
1066
1073
  if (!isNudgeUiDev()) {
1067
1074
  return {
1068
1075
  kind: "rejected",
@@ -5,6 +5,12 @@ const EMPTY_SELECTION = [];
5
5
  function isSelectedElementList(selected) {
6
6
  return Array.isArray(selected);
7
7
  }
8
+ const EMPTY_VIEW = {
9
+ element: null,
10
+ elements: [],
11
+ stableProperties: [],
12
+ documentTokens: null,
13
+ };
8
14
  function normalizeSelection(selected) {
9
15
  if (!selected)
10
16
  return EMPTY_SELECTION;
@@ -22,6 +28,9 @@ function inspectStableView(session, selected, state, includeDocumentTokens, elem
22
28
  : [];
23
29
  const documentTokens = includeDocumentTokens ? session.inspectTokens() : null;
24
30
  return {
31
+ selection: selected,
32
+ state,
33
+ session,
25
34
  element: primary,
26
35
  elements,
27
36
  stableProperties,
@@ -155,5 +164,10 @@ export function useBrowserCssInspection(selected, state = "base", options = {})
155
164
  const timeoutId = setTimeout(warm, 200);
156
165
  return () => clearTimeout(timeoutId);
157
166
  }, [session, selectedDocument]);
158
- return view;
167
+ // Selection effects run after children render. Never expose the previous
168
+ // element's authored rows while the new selection is waiting for inspection.
169
+ const matchesSelection = view.session === session && view.state === state
170
+ && view.selection.length === selectedElements.length
171
+ && view.selection.every((element, index) => element.domElement === selectedElements[index]?.domElement);
172
+ return matchesSelection ? view : EMPTY_VIEW;
159
173
  }
@@ -0,0 +1,2 @@
1
+ /** Captures the visible DOM of a same-origin frame as a script-free HTML iteration. */
2
+ export declare function captureHtmlIteration(frame: HTMLIFrameElement): Promise<string>;