nudge-ui 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) 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 +878 -11
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
  6. package/dist/hosts/astro/clientTransport.js +63 -11
  7. package/dist/hosts/astro/integration.d.ts +2 -2
  8. package/dist/hosts/astro/integration.js +4 -3
  9. package/dist/hosts/environment.d.ts +15 -0
  10. package/dist/hosts/environment.js +16 -0
  11. package/dist/hosts/next/sidecar.d.ts +4 -0
  12. package/dist/hosts/next/sidecar.js +56 -7
  13. package/dist/hosts/next/wrapper.d.ts +2 -0
  14. package/dist/hosts/next/wrapper.js +32 -3
  15. package/dist/hosts/projectBridge.d.ts +21 -0
  16. package/dist/hosts/projectBridge.js +98 -0
  17. package/dist/hosts/static/cli.d.ts +1 -1
  18. package/dist/hosts/static/cli.js +6 -1
  19. package/dist/hosts/static/manifest.d.ts +1 -1
  20. package/dist/hosts/static/manifest.js +3 -1
  21. package/dist/hosts/static/server.js +45 -3
  22. package/dist/hosts/vite/vite.d.ts +6 -1
  23. package/dist/hosts/vite/vite.js +82 -15
  24. package/dist/inspector/agent/McpConnectionDialog.d.ts +5 -4
  25. package/dist/inspector/agent/McpConnectionDialog.js +20 -37
  26. package/dist/inspector/agent/client.d.ts +16 -3
  27. package/dist/inspector/agent/client.js +114 -11
  28. package/dist/inspector/agent/connectionStatus.d.ts +3 -3
  29. package/dist/inspector/agent/connectionStatus.js +11 -4
  30. package/dist/inspector/agent/httpTransport.d.ts +8 -0
  31. package/dist/inspector/agent/httpTransport.js +78 -18
  32. package/dist/inspector/agent/protocol.d.ts +11 -3
  33. package/dist/inspector/agent/protocol.js +1 -1
  34. package/dist/inspector/agent/verification.d.ts +1 -1
  35. package/dist/inspector/agent/verification.js +69 -12
  36. package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
  37. package/dist/inspector/canvas/CanvasCard.js +181 -91
  38. package/dist/inspector/canvas/CanvasElementOverlay.js +288 -52
  39. package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
  40. package/dist/inspector/canvas/CanvasToolbar.js +24 -0
  41. package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
  42. package/dist/inspector/canvas/CanvasWorkspace.js +410 -72
  43. package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
  44. package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
  45. package/dist/inspector/canvas/canvasResize.d.ts +30 -0
  46. package/dist/inspector/canvas/canvasResize.js +52 -0
  47. package/dist/inspector/canvas/canvasStore.d.ts +30 -6
  48. package/dist/inspector/canvas/canvasStore.js +128 -61
  49. package/dist/inspector/canvas/frameProtocol.d.ts +82 -5
  50. package/dist/inspector/canvas/frameProtocol.js +63 -2
  51. package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -0
  52. package/dist/inspector/canvas/keyboardShortcuts.js +16 -0
  53. package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
  54. package/dist/inspector/canvas/linkEligibility.js +0 -29
  55. package/dist/inspector/canvas/projection.d.ts +7 -0
  56. package/dist/inspector/canvas/projection.js +118 -25
  57. package/dist/inspector/canvas/rendererBootstrap.js +42 -29
  58. package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
  59. package/dist/inspector/canvas/rendererElementSelector.js +385 -58
  60. package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
  61. package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
  62. package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
  63. package/dist/inspector/canvas/sessionStore.d.ts +1 -0
  64. package/dist/inspector/canvas/sessionStore.js +12 -4
  65. package/dist/inspector/canvas/staleChangeDetector.js +16 -14
  66. package/dist/inspector/changes/changesLog.js +31 -1
  67. package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
  68. package/dist/inspector/changes/managedStyleProjection.js +3 -3
  69. package/dist/inspector/changes/structuralTypes.d.ts +4 -0
  70. package/dist/inspector/changes/workspaceChanges.d.ts +4 -0
  71. package/dist/inspector/changes/workspaceChanges.js +29 -3
  72. package/dist/inspector/clientManifest.d.ts +7 -0
  73. package/dist/inspector/clientManifest.js +12 -0
  74. package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
  75. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
  76. package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
  77. package/dist/inspector/index.d.ts +2 -0
  78. package/dist/inspector/index.js +80 -32
  79. package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
  80. package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
  81. package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
  82. package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
  83. package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
  84. package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
  85. package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
  86. package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
  87. package/dist/inspector/inline-text/textEditChange.js +165 -0
  88. package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
  89. package/dist/inspector/inspection/selectionProperty.js +1 -0
  90. package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
  91. package/dist/inspector/overlay/DomNavigator.js +40 -0
  92. package/dist/inspector/overlay/InspectorOverlay.js +6 -2
  93. package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
  94. package/dist/inspector/overlay/clickPolicy.js +34 -3
  95. package/dist/inspector/overlay/elementSelector.d.ts +1 -1
  96. package/dist/inspector/overlay/elementSelector.js +18 -7
  97. package/dist/inspector/overlay/selectedGeometry.js +21 -2
  98. package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
  99. package/dist/inspector/overlay/spacingGestures.js +351 -0
  100. package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
  101. package/dist/inspector/overlay/structuralGestures.js +12 -10
  102. package/dist/inspector/projection/managedStylesheet.js +4 -1
  103. package/dist/inspector/projection/renderedInstance.d.ts +2 -0
  104. package/dist/inspector/projection/renderedInstance.js +5 -1
  105. package/dist/inspector/projection/structuralProjection.d.ts +2 -0
  106. package/dist/inspector/projection/structuralProjection.js +85 -1
  107. package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
  108. package/dist/inspector/projection/textProjection.d.ts +2 -0
  109. package/dist/inspector/projection/textProjection.js +10 -4
  110. package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
  111. package/dist/inspector/prompt/clipboardHandoff.js +3 -3
  112. package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
  113. package/dist/inspector/prompt/generatePrompt.js +19 -2
  114. package/dist/inspector/runtime/editorShell.d.ts +2 -0
  115. package/dist/inspector/runtime/editorShell.js +4 -0
  116. package/dist/inspector/runtime/runtimeConfig.d.ts +5 -1
  117. package/dist/inspector/runtime/runtimeConfig.js +4 -0
  118. package/dist/inspector/selection/hierarchy.d.ts +6 -0
  119. package/dist/inspector/selection/hierarchy.js +8 -0
  120. package/dist/inspector/selection/selectionStore.js +2 -0
  121. package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
  122. package/dist/inspector/settings/SettingsDialog.js +12 -2
  123. package/dist/inspector/shell/AppShell.js +5 -1
  124. package/dist/inspector/shell/ChangesLog.js +26 -22
  125. package/dist/inspector/shell/CopyPromptButton.js +102 -19
  126. package/dist/inspector/shell/EmptyState.js +1 -1
  127. package/dist/inspector/shell/InspectorShell.d.ts +0 -1
  128. package/dist/inspector/shell/InspectorShell.js +38 -42
  129. package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
  130. package/dist/inspector/shell/PeekOriginalButton.js +57 -0
  131. package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
  132. package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
  133. package/dist/inspector/shell/originalPreview.d.ts +9 -0
  134. package/dist/inspector/shell/originalPreview.js +61 -0
  135. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  136. package/dist/inspector/shell/shortcuts.js +15 -0
  137. package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
  138. package/dist/inspector/sketch/SketchChanges.js +72 -0
  139. package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
  140. package/dist/inspector/sketch/SketchEditor.js +402 -0
  141. package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
  142. package/dist/inspector/sketch/SketchEntryButton.js +11 -0
  143. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
  144. package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
  145. package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
  146. package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
  147. package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
  148. package/dist/inspector/sketch/SketchOverlay.js +420 -0
  149. package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
  150. package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
  151. package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
  152. package/dist/inspector/sketch/SketchWorkspace.js +152 -0
  153. package/dist/inspector/sketch/capture.d.ts +55 -0
  154. package/dist/inspector/sketch/capture.js +350 -0
  155. package/dist/inspector/sketch/freehand.d.ts +13 -0
  156. package/dist/inspector/sketch/freehand.js +64 -0
  157. package/dist/inspector/sketch/geometry.d.ts +17 -0
  158. package/dist/inspector/sketch/geometry.js +98 -0
  159. package/dist/inspector/sketch/handoff.d.ts +21 -0
  160. package/dist/inspector/sketch/handoff.js +72 -0
  161. package/dist/inspector/sketch/interaction.d.ts +21 -0
  162. package/dist/inspector/sketch/interaction.js +98 -0
  163. package/dist/inspector/sketch/model.d.ts +104 -0
  164. package/dist/inspector/sketch/model.js +282 -0
  165. package/dist/inspector/sketch/persistence.d.ts +21 -0
  166. package/dist/inspector/sketch/persistence.js +205 -0
  167. package/dist/inspector/sketch/raster.d.ts +22 -0
  168. package/dist/inspector/sketch/raster.js +203 -0
  169. package/dist/inspector/sketch/sketchNote.d.ts +10 -0
  170. package/dist/inspector/sketch/sketchNote.js +30 -0
  171. package/dist/inspector/sketch/store.d.ts +47 -0
  172. package/dist/inspector/sketch/store.js +434 -0
  173. package/dist/inspector/styleEditors/BorderEditor.js +15 -19
  174. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
  175. package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
  176. package/dist/inspector/styleEditors/ColorPicker.js +11 -36
  177. package/dist/inspector/styleEditors/GapField.d.ts +3 -2
  178. package/dist/inspector/styleEditors/GapField.js +2 -2
  179. package/dist/inspector/styleEditors/GridSection.js +1 -1
  180. package/dist/inspector/styleEditors/LayoutSection.js +11 -8
  181. package/dist/inspector/styleEditors/SpacingBox.js +31 -33
  182. package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
  183. package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
  184. package/dist/inspector/styleEditors/layoutValue.js +3 -0
  185. package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
  186. package/dist/inspector/styleEditors/nudgeValue.js +93 -5
  187. package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
  188. package/dist/inspector/styleEditors/stylePresence.js +22 -0
  189. package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
  190. package/dist/inspector/styleEditors/textRelevance.js +35 -0
  191. package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
  192. package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
  193. package/dist/inspector/tokens/TokenField.js +130 -7
  194. package/dist/inspector/tokens/TokensPanel.js +2 -1
  195. package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
  196. package/dist/inspector/tokens/dragPointerLock.js +70 -0
  197. package/dist/inspector/tokens/editActions.d.ts +2 -0
  198. package/dist/inspector/tokens/editActions.js +2 -2
  199. package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
  200. package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
  201. package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
  202. package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
  203. package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
  204. package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
  205. package/dist/inspector/tokens/resolution.d.ts +3 -26
  206. package/dist/inspector/tokens/resolution.js +44 -735
  207. package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
  208. package/dist/inspector/tokens/tokenAvailability.js +204 -0
  209. package/dist/inspector/ui/Button.d.ts +1 -1
  210. package/dist/inspector/ui/Disclosure.d.ts +22 -0
  211. package/dist/inspector/ui/Disclosure.js +13 -0
  212. package/dist/inspector/ui/SideValuesField.js +2 -2
  213. package/dist/inspector/ui/StatusCallout.d.ts +4 -1
  214. package/dist/inspector/ui/StatusCallout.js +2 -2
  215. package/dist/inspector/ui/dragCursor.d.ts +8 -0
  216. package/dist/inspector/ui/dragCursor.js +21 -0
  217. package/dist/inspector/ui/styles.js +21 -17
  218. package/dist/internal/agent-protocol.d.ts +65 -1
  219. package/dist/internal/agent-protocol.js +183 -3
  220. package/dist/nudge-ui.mjs +276 -58
  221. package/dist/transport/editor.d.ts +47 -0
  222. package/dist/transport/editor.js +163 -0
  223. package/dist/transport/index.d.ts +1 -0
  224. package/dist/transport/index.js +1 -0
  225. package/dist/transport/routes.d.ts +10 -0
  226. package/dist/transport/routes.js +10 -0
  227. package/package.json +3 -1
  228. package/dist/inspector/shell/DomNavigation.d.ts +0 -8
  229. package/dist/inspector/shell/DomNavigation.js +0 -30
  230. package/dist/inspector/shell/panelLayout.d.ts +0 -2
  231. package/dist/inspector/shell/panelLayout.js +0 -29
@@ -0,0 +1,72 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { blobToBase64 } from "./raster.js";
3
+ import { toAgentSketchMetadata } from "./model.js";
4
+ const EMPTY_HANDOFF = null;
5
+ let handoffSnapshot = EMPTY_HANDOFF;
6
+ const listeners = new Set();
7
+ function notify() {
8
+ for (const listener of listeners) {
9
+ try {
10
+ listener();
11
+ }
12
+ catch {
13
+ // A handoff view must not prevent other subscribers from updating.
14
+ }
15
+ }
16
+ }
17
+ function randomId() {
18
+ try {
19
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
20
+ return crypto.randomUUID();
21
+ }
22
+ catch {
23
+ // Use the fallback below.
24
+ }
25
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
26
+ }
27
+ export function createSketchHandoffSnapshot(items) {
28
+ if (items.length === 0)
29
+ return null;
30
+ return {
31
+ localBatchId: randomId(),
32
+ entries: items.map((item) => ({
33
+ id: item.document.id,
34
+ revision: item.document.revision,
35
+ metadata: toAgentSketchMetadata(item.document),
36
+ annotatedImage: item.document.annotatedImage,
37
+ })),
38
+ };
39
+ }
40
+ export function createSketchAttachments(handoff) {
41
+ return Promise.all(handoff.entries.map(async (entry) => ({
42
+ ...entry.metadata,
43
+ data: await blobToBase64(entry.annotatedImage),
44
+ })));
45
+ }
46
+ export function sketchMetadataForHandoff(handoff) {
47
+ return handoff?.entries.map((entry) => entry.metadata) ?? [];
48
+ }
49
+ export function getSketchClipboardHandoffSnapshot() {
50
+ return handoffSnapshot;
51
+ }
52
+ export function subscribeSketchClipboardHandoff(listener) {
53
+ listeners.add(listener);
54
+ return () => listeners.delete(listener);
55
+ }
56
+ export function useSketchClipboardHandoff() {
57
+ return useSyncExternalStore(subscribeSketchClipboardHandoff, getSketchClipboardHandoffSnapshot, getSketchClipboardHandoffSnapshot);
58
+ }
59
+ export function recordSketchClipboardHandoff(snapshot) {
60
+ handoffSnapshot = snapshot;
61
+ notify();
62
+ }
63
+ export function clearSketchClipboardHandoff() {
64
+ if (handoffSnapshot === null)
65
+ return;
66
+ handoffSnapshot = null;
67
+ notify();
68
+ }
69
+ export function resetSketchClipboardHandoff() {
70
+ handoffSnapshot = null;
71
+ notify();
72
+ }
@@ -0,0 +1,21 @@
1
+ import { type CapturedSketch } from "./capture.js";
2
+ export type SketchCaptureState = "idle" | "sketching" | "capturing" | "ready" | "error";
3
+ export type SketchEntryTool = "pen" | "annotate";
4
+ export interface SketchInteractionSnapshot {
5
+ readonly captureState: SketchCaptureState;
6
+ readonly captured: CapturedSketch | null;
7
+ readonly editingId: string | null;
8
+ readonly error: string | null;
9
+ readonly initialTool: SketchEntryTool;
10
+ }
11
+ export declare function getSketchInteractionSnapshot(): SketchInteractionSnapshot;
12
+ export declare function subscribeSketchInteraction(listener: () => void): () => void;
13
+ export declare function useSketchInteraction(): SketchInteractionSnapshot;
14
+ export declare function isSketchInteractionActive(): boolean;
15
+ export declare function useSketchInteractionActive(): boolean;
16
+ export declare function beginSketchCapture(initialTool?: SketchEntryTool): void;
17
+ export declare function completeSketchCapture(hostElement: HTMLElement): Promise<CapturedSketch>;
18
+ export declare function cancelSketchInteraction(): void;
19
+ export declare function openSketchEditor(sketchId: string): void;
20
+ export declare function closeSketchEditor(): void;
21
+ export declare function resetSketchInteraction(): void;
@@ -0,0 +1,98 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { captureViewport, } from "./capture.js";
3
+ const EMPTY_SNAPSHOT = {
4
+ captureState: "idle",
5
+ captured: null,
6
+ editingId: null,
7
+ error: null,
8
+ initialTool: "pen",
9
+ };
10
+ let snapshot = EMPTY_SNAPSHOT;
11
+ let captureController = null;
12
+ const listeners = new Set();
13
+ function publish(next) {
14
+ snapshot = next;
15
+ for (const listener of listeners) {
16
+ try {
17
+ listener();
18
+ }
19
+ catch {
20
+ // A view subscriber must not break the interaction state machine.
21
+ }
22
+ }
23
+ }
24
+ export function getSketchInteractionSnapshot() {
25
+ return snapshot;
26
+ }
27
+ export function subscribeSketchInteraction(listener) {
28
+ listeners.add(listener);
29
+ return () => listeners.delete(listener);
30
+ }
31
+ export function useSketchInteraction() {
32
+ return useSyncExternalStore(subscribeSketchInteraction, getSketchInteractionSnapshot, getSketchInteractionSnapshot);
33
+ }
34
+ export function isSketchInteractionActive() {
35
+ return snapshot.captureState !== "idle" || snapshot.captured !== null || snapshot.editingId !== null;
36
+ }
37
+ export function useSketchInteractionActive() {
38
+ const current = useSketchInteraction();
39
+ return current.captureState !== "idle" || current.captured !== null || current.editingId !== null;
40
+ }
41
+ export function beginSketchCapture(initialTool = "pen") {
42
+ captureController?.abort();
43
+ captureController = null;
44
+ publish({ captureState: "sketching", captured: null, editingId: null, error: null, initialTool });
45
+ }
46
+ export function completeSketchCapture(hostElement) {
47
+ captureController?.abort();
48
+ const controller = new AbortController();
49
+ captureController = controller;
50
+ const initialTool = snapshot.initialTool;
51
+ publish({ captureState: "capturing", captured: null, editingId: null, error: null, initialTool });
52
+ const options = { hostElement, signal: controller.signal };
53
+ return captureViewport(options)
54
+ .then((captured) => {
55
+ if (captureController !== controller)
56
+ return captured;
57
+ captureController = null;
58
+ publish({ captureState: "ready", captured, editingId: null, error: null, initialTool });
59
+ return captured;
60
+ })
61
+ .catch((error) => {
62
+ if (captureController !== controller)
63
+ throw error;
64
+ captureController = null;
65
+ if (controller.signal.aborted) {
66
+ publish(EMPTY_SNAPSHOT);
67
+ throw error;
68
+ }
69
+ publish({
70
+ captureState: "error",
71
+ captured: null,
72
+ editingId: null,
73
+ error: error instanceof Error ? error.message : "The viewport could not be captured.",
74
+ initialTool,
75
+ });
76
+ throw error;
77
+ });
78
+ }
79
+ export function cancelSketchInteraction() {
80
+ captureController?.abort();
81
+ captureController = null;
82
+ publish(EMPTY_SNAPSHOT);
83
+ }
84
+ export function openSketchEditor(sketchId) {
85
+ captureController?.abort();
86
+ captureController = null;
87
+ publish({ captureState: "idle", captured: null, editingId: sketchId, error: null, initialTool: "pen" });
88
+ }
89
+ export function closeSketchEditor() {
90
+ if (snapshot.captureState !== "ready" && snapshot.editingId === null)
91
+ return;
92
+ publish(EMPTY_SNAPSHOT);
93
+ }
94
+ export function resetSketchInteraction() {
95
+ captureController?.abort();
96
+ captureController = null;
97
+ snapshot = EMPTY_SNAPSHOT;
98
+ }
@@ -0,0 +1,104 @@
1
+ import type { AgentSketchCaptureMetadata, AgentSketchMetadata } from "../agent/protocol.js";
2
+ export declare const SKETCH_SCHEMA_VERSION: 1;
3
+ export declare const SKETCH_DATABASE_NAME = "nudge-ui-sketches";
4
+ export declare const SKETCH_DATABASE_VERSION = 1;
5
+ export declare const SKETCH_DOCUMENTS_STORE = "documents";
6
+ export declare const SKETCH_HANDOFFS_STORE = "handoffs";
7
+ export declare const SKETCH_LIMITS: {
8
+ readonly completedSketches: 4;
9
+ readonly imageEdge: 2048;
10
+ readonly imageBytes: number;
11
+ readonly projectBytes: number;
12
+ readonly strokes: 500;
13
+ readonly points: 20000;
14
+ readonly annotations: 50;
15
+ readonly description: 2000;
16
+ readonly url: 2048;
17
+ readonly title: 512;
18
+ };
19
+ export declare const SKETCH_STROKE_WIDTH = 6;
20
+ export declare const SKETCH_STROKE_COLOR = "#0096ff";
21
+ export declare const SKETCH_STROKE_OUTLINE = "";
22
+ export declare const SKETCH_ANNOTATION_RADIUS = 16;
23
+ declare const LEGACY_SKETCH_STROKE_COLOR = "#ef4444";
24
+ declare const LEGACY_SKETCH_STROKE_OUTLINE = "#111827";
25
+ export type SketchStatus = "pending" | "dispatching" | "handing-off" | "needs-review" | "unknown";
26
+ export type SketchTransport = "local" | "clipboard" | "direct";
27
+ export type SketchHandoffState = SketchStatus;
28
+ export interface SketchPoint {
29
+ readonly x: number;
30
+ readonly y: number;
31
+ }
32
+ type SketchStrokeColor = typeof SKETCH_STROKE_COLOR | typeof LEGACY_SKETCH_STROKE_COLOR;
33
+ type SketchStrokeOutline = typeof SKETCH_STROKE_OUTLINE | typeof LEGACY_SKETCH_STROKE_OUTLINE;
34
+ export type SketchStrokeKind = "freehand" | "rectangle";
35
+ export interface SketchStroke {
36
+ readonly id: string;
37
+ /** Optional for compatibility with strokes saved before movable layers. */
38
+ readonly kind?: SketchStrokeKind;
39
+ readonly width: number;
40
+ readonly color: SketchStrokeColor;
41
+ readonly outlineColor: SketchStrokeOutline;
42
+ readonly points: readonly SketchPoint[];
43
+ }
44
+ export interface SketchAnnotation {
45
+ readonly id: string;
46
+ readonly number: number;
47
+ readonly point: SketchPoint;
48
+ readonly description: string;
49
+ }
50
+ export interface SketchCaptureMetadata extends AgentSketchCaptureMetadata {
51
+ readonly imageWidth: number;
52
+ readonly imageHeight: number;
53
+ }
54
+ export interface SketchDocument {
55
+ readonly schemaVersion: typeof SKETCH_SCHEMA_VERSION;
56
+ readonly projectId: string;
57
+ readonly id: string;
58
+ readonly revision: number;
59
+ readonly createdAt: number;
60
+ readonly updatedAt: number;
61
+ readonly capture: SketchCaptureMetadata;
62
+ readonly description: string;
63
+ readonly strokes: readonly SketchStroke[];
64
+ /** Optional for compatibility with sketches saved before annotations existed. */
65
+ readonly annotations?: readonly SketchAnnotation[];
66
+ readonly imageWidth: number;
67
+ readonly imageHeight: number;
68
+ readonly originalByteSize: number;
69
+ readonly annotatedByteSize: number;
70
+ readonly filename: string;
71
+ readonly originalImage: Blob;
72
+ readonly annotatedImage: Blob;
73
+ }
74
+ export interface SketchHandoffRecord {
75
+ readonly id: string;
76
+ readonly projectId: string;
77
+ readonly sketchId: string;
78
+ readonly sketchRevision: number;
79
+ readonly localBatchId: string;
80
+ readonly batchRevision?: number;
81
+ readonly transport: SketchTransport;
82
+ readonly agentRequestId?: string;
83
+ readonly timestamp: number;
84
+ readonly state: SketchHandoffState;
85
+ }
86
+ export interface SketchQueueItem {
87
+ readonly document: SketchDocument;
88
+ readonly handoff: SketchHandoffRecord | null;
89
+ readonly status: SketchStatus;
90
+ }
91
+ export declare function createSketchFilename(id: string, revision: number): string;
92
+ export declare function isFiniteNumber(value: unknown): value is number;
93
+ export declare function isSketchCaptureMetadata(value: unknown): value is SketchCaptureMetadata;
94
+ export declare function isSketchPoint(value: unknown): value is SketchPoint;
95
+ export declare function isSketchAnnotation(value: unknown): value is SketchAnnotation;
96
+ export declare function isSketchStroke(value: unknown): value is SketchStroke;
97
+ export declare function isSketchDocument(value: unknown): value is SketchDocument;
98
+ export declare function isSketchHandoffRecord(value: unknown): value is SketchHandoffRecord;
99
+ export declare function cloneSketchDocument(document: SketchDocument): SketchDocument;
100
+ export declare function cloneSketchHandoff(handoff: SketchHandoffRecord): SketchHandoffRecord;
101
+ export declare function sketchContentFingerprint(description: string, strokes: readonly SketchStroke[], annotations?: readonly SketchAnnotation[]): string;
102
+ export declare function sameSketchContent(document: Pick<SketchDocument, "description" | "strokes" | "annotations">, description: string, strokes: readonly SketchStroke[], annotations?: readonly SketchAnnotation[]): boolean;
103
+ export declare function toAgentSketchMetadata(document: SketchDocument): AgentSketchMetadata;
104
+ export {};
@@ -0,0 +1,282 @@
1
+ export const SKETCH_SCHEMA_VERSION = 1;
2
+ export const SKETCH_DATABASE_NAME = "nudge-ui-sketches";
3
+ export const SKETCH_DATABASE_VERSION = 1;
4
+ export const SKETCH_DOCUMENTS_STORE = "documents";
5
+ export const SKETCH_HANDOFFS_STORE = "handoffs";
6
+ export const SKETCH_LIMITS = {
7
+ completedSketches: 4,
8
+ imageEdge: 2_048,
9
+ imageBytes: 2 * 1024 * 1024,
10
+ projectBytes: 20 * 1024 * 1024,
11
+ strokes: 500,
12
+ points: 20_000,
13
+ annotations: 50,
14
+ description: 2_000,
15
+ url: 2_048,
16
+ title: 512,
17
+ };
18
+ export const SKETCH_STROKE_WIDTH = 6;
19
+ export const SKETCH_STROKE_COLOR = "#0096ff";
20
+ export const SKETCH_STROKE_OUTLINE = "";
21
+ export const SKETCH_ANNOTATION_RADIUS = 16;
22
+ // Keep accepting sketches saved before the annotation color update. Existing
23
+ // PNGs remain unchanged until a user edits and saves them again.
24
+ const LEGACY_SKETCH_STROKE_COLOR = "#ef4444";
25
+ const LEGACY_SKETCH_STROKE_OUTLINE = "#111827";
26
+ export function createSketchFilename(id, revision) {
27
+ return `sketch-${id}-r${revision}.png`;
28
+ }
29
+ export function isFiniteNumber(value) {
30
+ return typeof value === "number" && Number.isFinite(value);
31
+ }
32
+ function boundedString(value, maximum, allowEmpty = false) {
33
+ return typeof value === "string" && value.length <= maximum && (allowEmpty || value.length > 0);
34
+ }
35
+ function isBlob(value, mimeType) {
36
+ return typeof Blob !== "undefined"
37
+ && value instanceof Blob
38
+ && (mimeType === undefined || value.type === mimeType);
39
+ }
40
+ function validHttpUrl(value, maximum) {
41
+ if (!boundedString(value, maximum))
42
+ return false;
43
+ try {
44
+ const url = new URL(value);
45
+ return url.protocol === "http:" || url.protocol === "https:";
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ export function isSketchCaptureMetadata(value) {
52
+ if (!value || typeof value !== "object" || Array.isArray(value))
53
+ return false;
54
+ const capture = value;
55
+ const keys = [
56
+ "url",
57
+ "title",
58
+ "timestamp",
59
+ "viewportWidth",
60
+ "viewportHeight",
61
+ "scrollX",
62
+ "scrollY",
63
+ "devicePixelRatio",
64
+ "host",
65
+ "framework",
66
+ "imageWidth",
67
+ "imageHeight",
68
+ ];
69
+ if (!Object.keys(capture).every((key) => keys.includes(key)))
70
+ return false;
71
+ return validHttpUrl(capture.url, SKETCH_LIMITS.url)
72
+ && boundedString(capture.title, SKETCH_LIMITS.title, true)
73
+ && Number.isSafeInteger(capture.timestamp)
74
+ && capture.timestamp > 0
75
+ && Number.isSafeInteger(capture.viewportWidth)
76
+ && capture.viewportWidth > 0
77
+ && capture.viewportWidth <= 100_000
78
+ && Number.isSafeInteger(capture.viewportHeight)
79
+ && capture.viewportHeight > 0
80
+ && capture.viewportHeight <= 100_000
81
+ && isFiniteNumber(capture.scrollX)
82
+ && capture.scrollX >= 0
83
+ && isFiniteNumber(capture.scrollY)
84
+ && capture.scrollY >= 0
85
+ && isFiniteNumber(capture.devicePixelRatio)
86
+ && capture.devicePixelRatio > 0
87
+ && capture.devicePixelRatio <= 100
88
+ && boundedString(capture.host, 128)
89
+ && boundedString(capture.framework, 128)
90
+ && Number.isSafeInteger(capture.imageWidth)
91
+ && capture.imageWidth > 0
92
+ && capture.imageWidth <= SKETCH_LIMITS.imageEdge
93
+ && Number.isSafeInteger(capture.imageHeight)
94
+ && capture.imageHeight > 0
95
+ && capture.imageHeight <= SKETCH_LIMITS.imageEdge;
96
+ }
97
+ export function isSketchPoint(value) {
98
+ if (!value || typeof value !== "object" || Array.isArray(value))
99
+ return false;
100
+ const point = value;
101
+ return Object.keys(point).every((key) => key === "x" || key === "y")
102
+ && isFiniteNumber(point.x)
103
+ && isFiniteNumber(point.y)
104
+ && point.x >= 0
105
+ && point.x <= SKETCH_LIMITS.imageEdge
106
+ && point.y >= 0
107
+ && point.y <= SKETCH_LIMITS.imageEdge;
108
+ }
109
+ export function isSketchAnnotation(value) {
110
+ if (!value || typeof value !== "object" || Array.isArray(value))
111
+ return false;
112
+ const annotation = value;
113
+ return Object.keys(annotation).every((key) => ["id", "number", "point", "description"].includes(key))
114
+ && boundedString(annotation.id, 256)
115
+ && Number.isSafeInteger(annotation.number)
116
+ && annotation.number >= 1
117
+ && annotation.number <= SKETCH_LIMITS.annotations
118
+ && isSketchPoint(annotation.point)
119
+ && boundedString(annotation.description, SKETCH_LIMITS.description);
120
+ }
121
+ export function isSketchStroke(value) {
122
+ if (!value || typeof value !== "object" || Array.isArray(value))
123
+ return false;
124
+ const stroke = value;
125
+ return Object.keys(stroke).every((key) => ["id", "kind", "width", "color", "outlineColor", "points"].includes(key))
126
+ && boundedString(stroke.id, 256)
127
+ && (stroke.kind === undefined || stroke.kind === "freehand" || stroke.kind === "rectangle")
128
+ && isFiniteNumber(stroke.width)
129
+ && stroke.width > 0
130
+ && stroke.width <= 128
131
+ && (stroke.color === SKETCH_STROKE_COLOR || stroke.color === LEGACY_SKETCH_STROKE_COLOR)
132
+ && (stroke.outlineColor === SKETCH_STROKE_OUTLINE || stroke.outlineColor === LEGACY_SKETCH_STROKE_OUTLINE)
133
+ && Array.isArray(stroke.points)
134
+ && stroke.points.length > 0
135
+ && stroke.points.length <= SKETCH_LIMITS.points
136
+ && stroke.points.every(isSketchPoint);
137
+ }
138
+ export function isSketchDocument(value) {
139
+ if (!value || typeof value !== "object" || Array.isArray(value))
140
+ return false;
141
+ const document = value;
142
+ return Object.keys(document).every((key) => [
143
+ "schemaVersion",
144
+ "projectId",
145
+ "id",
146
+ "revision",
147
+ "createdAt",
148
+ "updatedAt",
149
+ "capture",
150
+ "description",
151
+ "strokes",
152
+ "annotations",
153
+ "imageWidth",
154
+ "imageHeight",
155
+ "originalByteSize",
156
+ "annotatedByteSize",
157
+ "filename",
158
+ "originalImage",
159
+ "annotatedImage",
160
+ ].includes(key))
161
+ && document.schemaVersion === SKETCH_SCHEMA_VERSION
162
+ && boundedString(document.projectId, 256)
163
+ && boundedString(document.id, 256)
164
+ && Number.isSafeInteger(document.revision)
165
+ && document.revision > 0
166
+ && Number.isSafeInteger(document.createdAt)
167
+ && Number.isSafeInteger(document.updatedAt)
168
+ && isSketchCaptureMetadata(document.capture)
169
+ && boundedString(document.description, SKETCH_LIMITS.description, true)
170
+ && Array.isArray(document.strokes)
171
+ && document.strokes.length <= SKETCH_LIMITS.strokes
172
+ && document.strokes.every(isSketchStroke)
173
+ && document.strokes.reduce((total, stroke) => total + stroke.points.length, 0) <= SKETCH_LIMITS.points
174
+ && (document.annotations === undefined
175
+ || (Array.isArray(document.annotations)
176
+ && document.annotations.length <= SKETCH_LIMITS.annotations
177
+ && document.annotations.every(isSketchAnnotation)
178
+ && new Set(document.annotations.map((annotation) => annotation.number)).size === document.annotations.length))
179
+ && document.imageWidth === document.capture.imageWidth
180
+ && document.imageHeight === document.capture.imageHeight
181
+ && Number.isSafeInteger(document.originalByteSize)
182
+ && document.originalByteSize > 0
183
+ && document.originalByteSize <= SKETCH_LIMITS.imageBytes
184
+ && Number.isSafeInteger(document.annotatedByteSize)
185
+ && document.annotatedByteSize > 0
186
+ && document.annotatedByteSize <= SKETCH_LIMITS.imageBytes
187
+ && boundedString(document.filename, 256)
188
+ && isBlob(document.originalImage, "image/png")
189
+ && isBlob(document.annotatedImage, "image/png")
190
+ && document.originalImage.size === document.originalByteSize
191
+ && document.annotatedImage.size === document.annotatedByteSize;
192
+ }
193
+ export function isSketchHandoffRecord(value) {
194
+ if (!value || typeof value !== "object" || Array.isArray(value))
195
+ return false;
196
+ const handoff = value;
197
+ return Object.keys(handoff).every((key) => [
198
+ "id",
199
+ "projectId",
200
+ "sketchId",
201
+ "sketchRevision",
202
+ "localBatchId",
203
+ "batchRevision",
204
+ "transport",
205
+ "agentRequestId",
206
+ "timestamp",
207
+ "state",
208
+ ].includes(key))
209
+ && boundedString(handoff.id, 256)
210
+ && boundedString(handoff.projectId, 256)
211
+ && boundedString(handoff.sketchId, 256)
212
+ && Number.isSafeInteger(handoff.sketchRevision)
213
+ && handoff.sketchRevision > 0
214
+ && boundedString(handoff.localBatchId, 256)
215
+ && (handoff.batchRevision === undefined
216
+ || (Number.isSafeInteger(handoff.batchRevision) && handoff.batchRevision >= 0))
217
+ && (handoff.transport === "local" || handoff.transport === "clipboard" || handoff.transport === "direct")
218
+ && (handoff.agentRequestId === undefined || boundedString(handoff.agentRequestId, 256))
219
+ && Number.isSafeInteger(handoff.timestamp)
220
+ && (handoff.state === "pending"
221
+ || handoff.state === "dispatching"
222
+ || handoff.state === "handing-off"
223
+ || handoff.state === "needs-review"
224
+ || handoff.state === "unknown");
225
+ }
226
+ export function cloneSketchDocument(document) {
227
+ return {
228
+ ...document,
229
+ capture: { ...document.capture },
230
+ strokes: document.strokes.map((stroke) => ({
231
+ ...stroke,
232
+ points: stroke.points.map((point) => ({ ...point })),
233
+ })),
234
+ ...(document.annotations
235
+ ? {
236
+ annotations: document.annotations.map((annotation) => ({
237
+ ...annotation,
238
+ point: { ...annotation.point },
239
+ })),
240
+ }
241
+ : {}),
242
+ };
243
+ }
244
+ export function cloneSketchHandoff(handoff) {
245
+ return { ...handoff };
246
+ }
247
+ export function sketchContentFingerprint(description, strokes, annotations = []) {
248
+ return JSON.stringify({ description, strokes, annotations });
249
+ }
250
+ export function sameSketchContent(document, description, strokes, annotations = []) {
251
+ return sketchContentFingerprint(document.description, document.strokes, document.annotations ?? [])
252
+ === sketchContentFingerprint(description, strokes, annotations);
253
+ }
254
+ export function toAgentSketchMetadata(document) {
255
+ const annotations = document.annotations?.map((annotation) => ({
256
+ number: annotation.number,
257
+ description: annotation.description,
258
+ }));
259
+ return {
260
+ id: document.id,
261
+ revision: document.revision,
262
+ filename: document.filename,
263
+ mimeType: "image/png",
264
+ width: document.imageWidth,
265
+ height: document.imageHeight,
266
+ byteSize: document.annotatedByteSize,
267
+ capture: {
268
+ url: document.capture.url,
269
+ title: document.capture.title,
270
+ timestamp: document.capture.timestamp,
271
+ viewportWidth: document.capture.viewportWidth,
272
+ viewportHeight: document.capture.viewportHeight,
273
+ scrollX: document.capture.scrollX,
274
+ scrollY: document.capture.scrollY,
275
+ devicePixelRatio: document.capture.devicePixelRatio,
276
+ host: document.capture.host,
277
+ framework: document.capture.framework,
278
+ },
279
+ ...(document.description ? { description: document.description } : {}),
280
+ ...(annotations?.length ? { annotations } : {}),
281
+ };
282
+ }
@@ -0,0 +1,21 @@
1
+ import { type SketchDocument, type SketchHandoffRecord } from "./model.js";
2
+ export declare class SketchStorageError extends Error {
3
+ readonly code = "sketch_storage_error";
4
+ constructor(message: string, options?: {
5
+ cause?: unknown;
6
+ });
7
+ }
8
+ export interface SketchPersistenceSnapshot {
9
+ readonly documents: SketchDocument[];
10
+ readonly handoffs: SketchHandoffRecord[];
11
+ }
12
+ export declare function readSketchPersistence(projectId: string): Promise<SketchPersistenceSnapshot>;
13
+ /** Writes a document and its initial/current handoff atomically. */
14
+ export declare function writeSketchPersistence(projectId: string, document: SketchDocument, handoff: SketchHandoffRecord): Promise<void>;
15
+ export declare function writeSketchHandoff(projectId: string, handoff: SketchHandoffRecord): Promise<void>;
16
+ export declare function writeSketchHandoffs(projectId: string, handoffs: readonly SketchHandoffRecord[]): Promise<void>;
17
+ export declare function deleteSketchPersistence(projectId: string, sketchId: string): Promise<void>;
18
+ export declare function clearSketchPersistence(projectId: string): Promise<void>;
19
+ export declare function closeSketchDatabase(): Promise<void>;
20
+ /** Test seam for the no-IndexedDB fallback; it does not delete browser data. */
21
+ export declare function resetSketchMemory(): void;