nudge-ui 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/dist/cli/agentCommand.js +4 -28
  2. package/dist/client.mjs +863 -213
  3. package/dist/hosts/next/wrapper.js +1 -7
  4. package/dist/hosts/projectBridge.js +3 -17
  5. package/dist/inspector/agent/McpConnectionDialog.js +7 -3
  6. package/dist/inspector/agent/activity.d.ts +1 -1
  7. package/dist/inspector/agent/activity.js +3 -2
  8. package/dist/inspector/agent/client.d.ts +2 -2
  9. package/dist/inspector/agent/client.js +28 -29
  10. package/dist/inspector/agent/connectionStatus.d.ts +3 -1
  11. package/dist/inspector/agent/connectionStatus.js +25 -0
  12. package/dist/inspector/agent/httpTransport.js +31 -147
  13. package/dist/inspector/agent/protocol.d.ts +1 -1
  14. package/dist/inspector/agent/protocol.js +1 -1
  15. package/dist/inspector/agent/verification.d.ts +1 -1
  16. package/dist/inspector/agent/verification.js +7 -4
  17. package/dist/inspector/boot.d.ts +3 -0
  18. package/dist/inspector/boot.js +297 -0
  19. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +2 -1
  20. package/dist/inspector/canvas/AgentActivityOverlay.js +6 -3
  21. package/dist/inspector/canvas/CanvasCard.js +21 -12
  22. package/dist/inspector/canvas/CanvasElementOverlay.js +82 -127
  23. package/dist/inspector/canvas/CanvasToolbar.js +2 -1
  24. package/dist/inspector/canvas/CanvasWorkspace.js +74 -97
  25. package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +3 -2
  26. package/dist/inspector/canvas/LockedWorkspaceNotice.js +10 -6
  27. package/dist/inspector/canvas/PageCard.d.ts +10 -0
  28. package/dist/inspector/canvas/PageCard.js +51 -0
  29. package/dist/inspector/canvas/SketchToolOptions.d.ts +5 -0
  30. package/dist/inspector/canvas/SketchToolOptions.js +86 -0
  31. package/dist/inspector/canvas/StaleChangeIndicator.d.ts +1 -1
  32. package/dist/inspector/canvas/StaleChangeIndicator.js +1 -1
  33. package/dist/inspector/canvas/ToolOptionsDrawer.d.ts +21 -0
  34. package/dist/inspector/canvas/ToolOptionsDrawer.js +77 -0
  35. package/dist/inspector/canvas/activeCanvasDocument.d.ts +3 -2
  36. package/dist/inspector/canvas/assets/eraser.svg +28 -0
  37. package/dist/inspector/canvas/assets/pen.svg +25 -0
  38. package/dist/inspector/canvas/canvasFrame.d.ts +16 -0
  39. package/dist/inspector/canvas/canvasFrame.js +10 -0
  40. package/dist/inspector/canvas/canvasGestures.d.ts +0 -1
  41. package/dist/inspector/canvas/canvasGestures.js +0 -6
  42. package/dist/inspector/canvas/canvasStore.d.ts +6 -0
  43. package/dist/inspector/canvas/canvasStore.js +36 -1
  44. package/dist/inspector/canvas/frameConnection.d.ts +48 -0
  45. package/dist/inspector/canvas/frameConnection.js +73 -0
  46. package/dist/inspector/canvas/frameProtocol.d.ts +18 -1
  47. package/dist/inspector/canvas/frameProtocol.js +36 -0
  48. package/dist/inspector/canvas/pageSurface.d.ts +11 -0
  49. package/dist/inspector/canvas/pageSurface.js +34 -0
  50. package/dist/inspector/canvas/projection.d.ts +19 -9
  51. package/dist/inspector/canvas/projection.js +58 -31
  52. package/dist/inspector/canvas/rendererBootstrap.js +4 -1
  53. package/dist/inspector/canvas/rendererElementSelector.js +9 -1
  54. package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
  55. package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
  56. package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
  57. package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
  58. package/dist/inspector/canvas/rendererStylesheet.js +15 -0
  59. package/dist/inspector/canvas/sessionStore.d.ts +2 -2
  60. package/dist/inspector/canvas/sessionStore.js +12 -9
  61. package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
  62. package/dist/inspector/canvas/staleChangeDetector.js +10 -22
  63. package/dist/inspector/changes/codecs.d.ts +1 -0
  64. package/dist/inspector/changes/codecs.js +20 -5
  65. package/dist/inspector/changes/draftPreview.d.ts +12 -0
  66. package/dist/inspector/changes/draftPreview.js +133 -0
  67. package/dist/inspector/changes/editModel.d.ts +8 -0
  68. package/dist/inspector/changes/editModel.js +8 -2
  69. package/dist/inspector/changes/managedStyleProjection.js +2 -1
  70. package/dist/inspector/changes/previewDiagnostics.js +1 -1
  71. package/dist/inspector/changes/types.d.ts +11 -1
  72. package/dist/inspector/comments/CommentsOverlay.js +18 -16
  73. package/dist/inspector/comments/store.js +3 -2
  74. package/dist/inspector/comments/verification.js +1 -1
  75. package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
  76. package/dist/inspector/componentSemantics/changeAction.js +1 -1
  77. package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
  78. package/dist/inspector/componentSemantics/textBinding.js +4 -45
  79. package/dist/inspector/drafts/draftStore.d.ts +109 -0
  80. package/dist/inspector/drafts/draftStore.js +451 -0
  81. package/dist/inspector/drafts/model.d.ts +9 -1
  82. package/dist/inspector/drafts/model.js +5 -1
  83. package/dist/inspector/drafts/store.d.ts +53 -15
  84. package/dist/inspector/drafts/store.js +143 -169
  85. package/dist/inspector/index.d.ts +2 -4
  86. package/dist/inspector/index.js +2 -264
  87. package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
  88. package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
  89. package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
  90. package/dist/inspector/inspection/bridge.d.ts +1 -1
  91. package/dist/inspector/inspection/bridge.js +2 -1
  92. package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
  93. package/dist/inspector/inspection/browserCssInspection.js +28 -6
  94. package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
  95. package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
  96. package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
  97. package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
  98. package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
  99. package/dist/inspector/iterations/capture.d.ts +2 -1
  100. package/dist/inspector/iterations/client.d.ts +2 -1
  101. package/dist/inspector/iterations/coordinator.d.ts +4 -3
  102. package/dist/inspector/iterations/coordinator.js +5 -3
  103. package/dist/inspector/iterations/prompt.js +9 -8
  104. package/dist/inspector/overlay/interactionStyles.js +17 -0
  105. package/dist/inspector/pageInspector.d.ts +42 -0
  106. package/dist/inspector/pageInspector.js +342 -0
  107. package/dist/inspector/projection/draftProjection.d.ts +10 -2
  108. package/dist/inspector/projection/draftProjection.js +9 -1
  109. package/dist/inspector/projection/managedStylesheet.js +24 -4
  110. package/dist/inspector/projection/renderedInstance.js +7 -1
  111. package/dist/inspector/projection/structuralProjection.d.ts +0 -9
  112. package/dist/inspector/projection/structuralProjection.js +14 -44
  113. package/dist/inspector/projection/textProjection.d.ts +8 -2
  114. package/dist/inspector/projection/textProjection.js +21 -13
  115. package/dist/inspector/prompt/clipboardHandoff.d.ts +9 -0
  116. package/dist/inspector/prompt/clipboardHandoff.js +42 -16
  117. package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
  118. package/dist/inspector/prompt/generatePrompt.js +243 -147
  119. package/dist/inspector/prompt/promptDocument.d.ts +3 -0
  120. package/dist/inspector/prompt/promptDocument.js +8 -0
  121. package/dist/inspector/prompt/promptSettings.js +7 -5
  122. package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
  123. package/dist/inspector/runtime/inspectorStorage.js +42 -0
  124. package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
  125. package/dist/inspector/runtime/ownedStylesheets.js +72 -0
  126. package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
  127. package/dist/inspector/runtime/runtimeConfig.js +10 -1
  128. package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
  129. package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
  130. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
  131. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
  132. package/dist/inspector/selection/editScope.d.ts +1 -0
  133. package/dist/inspector/selection/editScope.js +13 -2
  134. package/dist/inspector/shell/AgentStatusButton.js +2 -2
  135. package/dist/inspector/shell/AppShell.js +6 -2
  136. package/dist/inspector/shell/ChangesLog.js +27 -9
  137. package/dist/inspector/shell/CopyPromptButton.js +60 -54
  138. package/dist/inspector/shell/EmptyState.js +3 -3
  139. package/dist/inspector/shell/InspectorShell.d.ts +0 -3
  140. package/dist/inspector/shell/InspectorShell.js +21 -13
  141. package/dist/inspector/shell/PeekOriginalButton.js +3 -3
  142. package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
  143. package/dist/inspector/shell/interactionStatePreview.js +112 -0
  144. package/dist/inspector/shell/originalPreview.d.ts +3 -2
  145. package/dist/inspector/shell/originalPreview.js +10 -37
  146. package/dist/inspector/shell/stateValue.js +2 -3
  147. package/dist/inspector/sketch/SketchEditor.js +5 -4
  148. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
  149. package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
  150. package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
  151. package/dist/inspector/sketch/SketchOverlay.js +139 -263
  152. package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
  153. package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
  154. package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
  155. package/dist/inspector/sketch/SketchThumbnail.js +18 -0
  156. package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
  157. package/dist/inspector/sketch/SketchWorkspace.js +69 -34
  158. package/dist/inspector/sketch/capture.js +19 -3
  159. package/dist/inspector/sketch/copySketch.d.ts +10 -0
  160. package/dist/inspector/sketch/copySketch.js +52 -0
  161. package/dist/inspector/sketch/drawingTools.d.ts +17 -0
  162. package/dist/inspector/sketch/drawingTools.js +47 -0
  163. package/dist/inspector/sketch/frameHeading.d.ts +9 -0
  164. package/dist/inspector/sketch/frameHeading.js +30 -0
  165. package/dist/inspector/sketch/freehand.js +3 -3
  166. package/dist/inspector/sketch/geometry.d.ts +6 -0
  167. package/dist/inspector/sketch/geometry.js +19 -0
  168. package/dist/inspector/sketch/history.d.ts +30 -0
  169. package/dist/inspector/sketch/history.js +58 -0
  170. package/dist/inspector/sketch/interaction.d.ts +1 -3
  171. package/dist/inspector/sketch/interaction.js +6 -9
  172. package/dist/inspector/sketch/model.d.ts +35 -1
  173. package/dist/inspector/sketch/model.js +116 -61
  174. package/dist/inspector/sketch/persistence.d.ts +14 -0
  175. package/dist/inspector/sketch/persistence.js +63 -7
  176. package/dist/inspector/sketch/raster.d.ts +6 -2
  177. package/dist/inspector/sketch/raster.js +43 -17
  178. package/dist/inspector/sketch/store.d.ts +1 -1
  179. package/dist/inspector/sketch/store.js +1 -1
  180. package/dist/inspector/sketch/videoFrames.d.ts +6 -0
  181. package/dist/inspector/sketch/videoFrames.js +81 -0
  182. package/dist/inspector/styleEditors/BackgroundEditor.js +2 -1
  183. package/dist/inspector/styleEditors/BorderEditor.js +30 -130
  184. package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
  185. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
  186. package/dist/inspector/styleEditors/LayoutSection.js +2 -2
  187. package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
  188. package/dist/inspector/styleEditors/borderModel.js +114 -0
  189. package/dist/inspector/styleEditors/layoutValue.js +2 -3
  190. package/dist/inspector/tokens/TokenField.js +34 -3
  191. package/dist/inspector/tokens/TokensPanel.js +2 -1
  192. package/dist/inspector/tokens/editActions.d.ts +3 -3
  193. package/dist/inspector/tokens/editActions.js +9 -3
  194. package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
  195. package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
  196. package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
  197. package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
  198. package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
  199. package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
  200. package/dist/inspector/tokens/resolution.d.ts +6 -3
  201. package/dist/inspector/tokens/resolution.js +91 -112
  202. package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
  203. package/dist/inspector/tokens/tokenEdits.js +1 -1
  204. package/dist/inspector/ui/InlineStyleWarning.js +2 -2
  205. package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
  206. package/dist/inspector/ui/InspectorStyle.js +26 -0
  207. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  208. package/dist/inspector/ui/PopoverListbox.js +3 -3
  209. package/dist/inspector/ui/Toast.d.ts +14 -0
  210. package/dist/inspector/ui/Toast.js +30 -0
  211. package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
  212. package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
  213. package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
  214. package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
  215. package/dist/inspector/ui/customCursors.d.ts +5 -0
  216. package/dist/inspector/ui/customCursors.js +32 -6
  217. package/dist/inspector/ui/styles.js +12 -10
  218. package/dist/inspector/workspace/commands.js +1 -2
  219. package/dist/inspector/workspace/controller.js +1 -2
  220. package/dist/inspector/workspace/handoff.js +3 -2
  221. package/dist/inspector/workspace/sentItems.d.ts +3 -6
  222. package/dist/inspector/workspace/sentItems.js +6 -24
  223. package/dist/inspector/workspace/timeline.d.ts +24 -10
  224. package/dist/inspector/workspace/timeline.js +118 -96
  225. package/dist/internal/agent-protocol.d.ts +50 -13
  226. package/dist/internal/agent-protocol.js +145 -19
  227. package/dist/nudge-ui.mjs +81 -65
  228. package/dist/project/mcpPackage.d.ts +8 -0
  229. package/dist/project/mcpPackage.js +60 -0
  230. package/package.json +12 -2
  231. package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
  232. package/dist/inspector/canvas/measurementProjection.js +0 -16
  233. package/dist/inspector/changes/changesLog.d.ts +0 -49
  234. package/dist/inspector/changes/changesLog.js +0 -255
  235. package/dist/inspector/changes/draftChanges.d.ts +0 -58
  236. package/dist/inspector/changes/draftChanges.js +0 -304
  237. package/dist/inspector/conformance/projection.d.ts +0 -2
  238. package/dist/inspector/conformance/projection.js +0 -3
  239. package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
  240. package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
  241. package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
  242. package/dist/inspector/overlay/InspectorOverlay.js +0 -332
  243. package/dist/inspector/overlay/elementSelector.d.ts +0 -1
  244. package/dist/inspector/overlay/elementSelector.js +0 -111
  245. package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
  246. package/dist/inspector/sketch/SketchChanges.js +0 -72
  247. package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
  248. package/dist/inspector/sketch/SketchEntryButton.js +0 -11
  249. package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
  250. package/dist/inspector/sketch/SketchLayersPanel.js +0 -71
@@ -17,8 +17,42 @@ export declare const SKETCH_LIMITS: {
17
17
  readonly title: 512;
18
18
  };
19
19
  export declare const SKETCH_STROKE_WIDTH = 6;
20
+ /** Drawn rectangles use a lighter outline than the freehand pen. */
21
+ export declare const SKETCH_RECTANGLE_STROKE_WIDTH = 4;
22
+ /** Opacity of the tint inside a drawn rectangle, in the rectangle's own color. */
23
+ export declare const SKETCH_RECTANGLE_FILL_OPACITY = 0.1;
24
+ /** Default pen color, and the fallback for strokes saved without one. */
20
25
  export declare const SKETCH_STROKE_COLOR = "#0096ff";
26
+ /** Annotation markers and the notes panel; independent of the chosen pen color. */
27
+ export declare const SKETCH_ACCENT_COLOR = "#0096ff";
21
28
  export declare const SKETCH_STROKE_OUTLINE = "";
29
+ /** Pen palette offered by the canvas toolbar, in clockwise order around its swatch picker. */
30
+ export declare const SKETCH_PEN_COLORS: readonly [{
31
+ readonly id: "red";
32
+ readonly name: "Red";
33
+ readonly value: "#f03e1e";
34
+ }, {
35
+ readonly id: "orange";
36
+ readonly name: "Orange";
37
+ readonly value: "#ff8a00";
38
+ }, {
39
+ readonly id: "blue";
40
+ readonly name: "Blue";
41
+ readonly value: "#0096ff";
42
+ }, {
43
+ readonly id: "black";
44
+ readonly name: "Black";
45
+ readonly value: "#242424";
46
+ }, {
47
+ readonly id: "yellow";
48
+ readonly name: "Yellow";
49
+ readonly value: "#ffc800";
50
+ }, {
51
+ readonly id: "green";
52
+ readonly name: "Green";
53
+ readonly value: "#1fc85a";
54
+ }];
55
+ export type SketchPenColor = (typeof SKETCH_PEN_COLORS)[number];
22
56
  export declare const SKETCH_ANNOTATION_RADIUS = 16;
23
57
  declare const LEGACY_SKETCH_STROKE_COLOR = "#ef4444";
24
58
  declare const LEGACY_SKETCH_STROKE_OUTLINE = "#111827";
@@ -29,7 +63,7 @@ export interface SketchPoint {
29
63
  readonly x: number;
30
64
  readonly y: number;
31
65
  }
32
- type SketchStrokeColor = typeof SKETCH_STROKE_COLOR | typeof LEGACY_SKETCH_STROKE_COLOR;
66
+ export type SketchStrokeColor = SketchPenColor["value"] | typeof LEGACY_SKETCH_STROKE_COLOR;
33
67
  type SketchStrokeOutline = typeof SKETCH_STROKE_OUTLINE | typeof LEGACY_SKETCH_STROKE_OUTLINE;
34
68
  export type SketchStrokeKind = "freehand" | "rectangle";
35
69
  export interface SketchStroke {
@@ -16,8 +16,24 @@ export const SKETCH_LIMITS = {
16
16
  title: 512,
17
17
  };
18
18
  export const SKETCH_STROKE_WIDTH = 6;
19
+ /** Drawn rectangles use a lighter outline than the freehand pen. */
20
+ export const SKETCH_RECTANGLE_STROKE_WIDTH = 4;
21
+ /** Opacity of the tint inside a drawn rectangle, in the rectangle's own color. */
22
+ export const SKETCH_RECTANGLE_FILL_OPACITY = 0.1;
23
+ /** Default pen color, and the fallback for strokes saved without one. */
19
24
  export const SKETCH_STROKE_COLOR = "#0096ff";
25
+ /** Annotation markers and the notes panel; independent of the chosen pen color. */
26
+ export const SKETCH_ACCENT_COLOR = "#0096ff";
20
27
  export const SKETCH_STROKE_OUTLINE = "";
28
+ /** Pen palette offered by the canvas toolbar, in clockwise order around its swatch picker. */
29
+ export const SKETCH_PEN_COLORS = [
30
+ { id: "red", name: "Red", value: "#f03e1e" },
31
+ { id: "orange", name: "Orange", value: "#ff8a00" },
32
+ { id: "blue", name: "Blue", value: SKETCH_STROKE_COLOR },
33
+ { id: "black", name: "Black", value: "#242424" },
34
+ { id: "yellow", name: "Yellow", value: "#ffc800" },
35
+ { id: "green", name: "Green", value: "#1fc85a" },
36
+ ];
21
37
  export const SKETCH_ANNOTATION_RADIUS = 16;
22
38
  // Keep accepting sketches saved before the annotation color update. Existing
23
39
  // PNGs remain unchanged until a user edits and saves them again.
@@ -26,18 +42,36 @@ const LEGACY_SKETCH_STROKE_OUTLINE = "#111827";
26
42
  export function createSketchFilename(id, revision) {
27
43
  return `sketch-${id}-r${revision}.png`;
28
44
  }
29
- export function isFiniteNumber(value) {
45
+ export function isFiniteNumber(
46
+ // Stored sketch fields are untrusted until this check narrows them.
47
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
48
+ value) {
30
49
  return typeof value === "number" && Number.isFinite(value);
31
50
  }
32
- function boundedString(value, maximum, allowEmpty = false) {
51
+ function safeIntegerInRange(
52
+ // Stored sketch fields are untrusted until this check narrows them.
53
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
54
+ value, minimum, maximum = Number.MAX_SAFE_INTEGER) {
55
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= minimum && value <= maximum;
56
+ }
57
+ function boundedString(
58
+ // Stored sketch fields are untrusted until this check narrows them.
59
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
60
+ value, maximum, allowEmpty = false) {
33
61
  return typeof value === "string" && value.length <= maximum && (allowEmpty || value.length > 0);
34
62
  }
35
- function isBlob(value, mimeType) {
63
+ function isBlob(
64
+ // Stored sketch images are untrusted until this check narrows them.
65
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
66
+ value, mimeType) {
36
67
  return typeof Blob !== "undefined"
37
68
  && value instanceof Blob
38
69
  && (mimeType === undefined || value.type === mimeType);
39
70
  }
40
- function validHttpUrl(value, maximum) {
71
+ function validHttpUrl(
72
+ // Stored sketch fields are untrusted until this check narrows them.
73
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
74
+ value, maximum) {
41
75
  if (!boundedString(value, maximum))
42
76
  return false;
43
77
  try {
@@ -48,9 +82,19 @@ function validHttpUrl(value, maximum) {
48
82
  return false;
49
83
  }
50
84
  }
51
- export function isSketchCaptureMetadata(value) {
52
- if (!value || typeof value !== "object" || Array.isArray(value))
85
+ function isPlainObject(
86
+ // Stored sketch records are untrusted until this check narrows them.
87
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
88
+ value) {
89
+ return typeof value === "object" && value !== null && !Array.isArray(value);
90
+ }
91
+ export function isSketchCaptureMetadata(
92
+ // Capture metadata is read back from storage and parsed here.
93
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
94
+ value) {
95
+ if (!isPlainObject(value))
53
96
  return false;
97
+ // SAFETY: the object check above makes reading optional candidate fields safe.
54
98
  const capture = value;
55
99
  const keys = [
56
100
  "url",
@@ -70,14 +114,9 @@ export function isSketchCaptureMetadata(value) {
70
114
  return false;
71
115
  return validHttpUrl(capture.url, SKETCH_LIMITS.url)
72
116
  && 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
117
+ && safeIntegerInRange(capture.timestamp, 1)
118
+ && safeIntegerInRange(capture.viewportWidth, 1, 100_000)
119
+ && safeIntegerInRange(capture.viewportHeight, 1, 100_000)
81
120
  && isFiniteNumber(capture.scrollX)
82
121
  && capture.scrollX >= 0
83
122
  && isFiniteNumber(capture.scrollY)
@@ -87,16 +126,16 @@ export function isSketchCaptureMetadata(value) {
87
126
  && capture.devicePixelRatio <= 100
88
127
  && boundedString(capture.host, 128)
89
128
  && 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;
129
+ && safeIntegerInRange(capture.imageWidth, 1, SKETCH_LIMITS.imageEdge)
130
+ && safeIntegerInRange(capture.imageHeight, 1, SKETCH_LIMITS.imageEdge);
96
131
  }
97
- export function isSketchPoint(value) {
98
- if (!value || typeof value !== "object" || Array.isArray(value))
132
+ export function isSketchPoint(
133
+ // Stored points are untrusted until this check narrows them.
134
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
135
+ value) {
136
+ if (!isPlainObject(value))
99
137
  return false;
138
+ // SAFETY: the object check above makes reading optional candidate fields safe.
100
139
  const point = value;
101
140
  return Object.keys(point).every((key) => key === "x" || key === "y")
102
141
  && isFiniteNumber(point.x)
@@ -106,21 +145,27 @@ export function isSketchPoint(value) {
106
145
  && point.y >= 0
107
146
  && point.y <= SKETCH_LIMITS.imageEdge;
108
147
  }
109
- export function isSketchAnnotation(value) {
110
- if (!value || typeof value !== "object" || Array.isArray(value))
148
+ export function isSketchAnnotation(
149
+ // Stored annotations are untrusted until this check narrows them.
150
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
151
+ value) {
152
+ if (!isPlainObject(value))
111
153
  return false;
154
+ // SAFETY: the object check above makes reading optional candidate fields safe.
112
155
  const annotation = value;
113
156
  return Object.keys(annotation).every((key) => ["id", "number", "point", "description"].includes(key))
114
157
  && boundedString(annotation.id, 256)
115
- && Number.isSafeInteger(annotation.number)
116
- && annotation.number >= 1
117
- && annotation.number <= SKETCH_LIMITS.annotations
158
+ && safeIntegerInRange(annotation.number, 1, SKETCH_LIMITS.annotations)
118
159
  && isSketchPoint(annotation.point)
119
160
  && boundedString(annotation.description, SKETCH_LIMITS.description);
120
161
  }
121
- export function isSketchStroke(value) {
122
- if (!value || typeof value !== "object" || Array.isArray(value))
162
+ export function isSketchStroke(
163
+ // Stored strokes are untrusted until this check narrows them.
164
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
165
+ value) {
166
+ if (!isPlainObject(value))
123
167
  return false;
168
+ // SAFETY: the object check above makes reading optional candidate fields safe.
124
169
  const stroke = value;
125
170
  return Object.keys(stroke).every((key) => ["id", "kind", "width", "color", "outlineColor", "points"].includes(key))
126
171
  && boundedString(stroke.id, 256)
@@ -128,16 +173,37 @@ export function isSketchStroke(value) {
128
173
  && isFiniteNumber(stroke.width)
129
174
  && stroke.width > 0
130
175
  && stroke.width <= 128
131
- && (stroke.color === SKETCH_STROKE_COLOR || stroke.color === LEGACY_SKETCH_STROKE_COLOR)
176
+ && (SKETCH_PEN_COLORS.some((color) => color.value === stroke.color)
177
+ || stroke.color === LEGACY_SKETCH_STROKE_COLOR)
132
178
  && (stroke.outlineColor === SKETCH_STROKE_OUTLINE || stroke.outlineColor === LEGACY_SKETCH_STROKE_OUTLINE)
133
179
  && Array.isArray(stroke.points)
134
180
  && stroke.points.length > 0
135
181
  && stroke.points.length <= SKETCH_LIMITS.points
136
182
  && stroke.points.every(isSketchPoint);
137
183
  }
138
- export function isSketchDocument(value) {
139
- if (!value || typeof value !== "object" || Array.isArray(value))
184
+ function isSketchStrokeList(
185
+ // Stored stroke lists are untrusted until this check narrows them.
186
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
187
+ value) {
188
+ return Array.isArray(value)
189
+ && value.length <= SKETCH_LIMITS.strokes
190
+ && value.every(isSketchStroke);
191
+ }
192
+ function isSketchAnnotationList(
193
+ // Stored annotation lists are untrusted until this check narrows them.
194
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
195
+ value) {
196
+ return Array.isArray(value)
197
+ && value.length <= SKETCH_LIMITS.annotations
198
+ && value.every(isSketchAnnotation);
199
+ }
200
+ export function isSketchDocument(
201
+ // Sketch documents are read back from storage and parsed here.
202
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
203
+ value) {
204
+ if (!isPlainObject(value))
140
205
  return false;
206
+ // SAFETY: the object check above makes reading optional candidate fields safe.
141
207
  const document = value;
142
208
  return Object.keys(document).every((key) => [
143
209
  "schemaVersion",
@@ -161,42 +227,33 @@ export function isSketchDocument(value) {
161
227
  && document.schemaVersion === SKETCH_SCHEMA_VERSION
162
228
  && boundedString(document.projectId, 256)
163
229
  && boundedString(document.id, 256)
164
- && Number.isSafeInteger(document.revision)
165
- && document.revision > 0
166
- && Number.isSafeInteger(document.createdAt)
167
- && Number.isSafeInteger(document.updatedAt)
230
+ && safeIntegerInRange(document.revision, 1)
231
+ && safeIntegerInRange(document.createdAt, Number.MIN_SAFE_INTEGER)
232
+ && safeIntegerInRange(document.updatedAt, Number.MIN_SAFE_INTEGER)
168
233
  && isSketchCaptureMetadata(document.capture)
169
234
  && boundedString(document.description, SKETCH_LIMITS.description, true)
170
- && Array.isArray(document.strokes)
171
- && document.strokes.length <= SKETCH_LIMITS.strokes
172
- && document.strokes.every(isSketchStroke)
235
+ && isSketchStrokeList(document.strokes)
173
236
  && document.strokes.reduce((total, stroke) => total + stroke.points.length, 0) <= SKETCH_LIMITS.points
174
237
  && (document.annotations === undefined
175
- || (Array.isArray(document.annotations)
176
- && document.annotations.length <= SKETCH_LIMITS.annotations
177
- && document.annotations.every(isSketchAnnotation)
238
+ || (isSketchAnnotationList(document.annotations)
178
239
  && new Set(document.annotations.map((annotation) => annotation.number)).size === document.annotations.length))
179
- && Number.isSafeInteger(document.imageWidth)
180
- && document.imageWidth > 0
181
- && document.imageWidth <= SKETCH_LIMITS.imageEdge
182
- && Number.isSafeInteger(document.imageHeight)
183
- && document.imageHeight > 0
184
- && document.imageHeight <= SKETCH_LIMITS.imageEdge
185
- && Number.isSafeInteger(document.originalByteSize)
186
- && document.originalByteSize > 0
187
- && document.originalByteSize <= SKETCH_LIMITS.imageBytes
188
- && Number.isSafeInteger(document.annotatedByteSize)
189
- && document.annotatedByteSize > 0
190
- && document.annotatedByteSize <= SKETCH_LIMITS.imageBytes
240
+ && safeIntegerInRange(document.imageWidth, 1, SKETCH_LIMITS.imageEdge)
241
+ && safeIntegerInRange(document.imageHeight, 1, SKETCH_LIMITS.imageEdge)
242
+ && safeIntegerInRange(document.originalByteSize, 1, SKETCH_LIMITS.imageBytes)
243
+ && safeIntegerInRange(document.annotatedByteSize, 1, SKETCH_LIMITS.imageBytes)
191
244
  && boundedString(document.filename, 256)
192
245
  && isBlob(document.originalImage, "image/png")
193
246
  && isBlob(document.annotatedImage, "image/png")
194
247
  && document.originalImage.size === document.originalByteSize
195
248
  && document.annotatedImage.size === document.annotatedByteSize;
196
249
  }
197
- export function isSketchHandoffRecord(value) {
198
- if (!value || typeof value !== "object" || Array.isArray(value))
250
+ export function isSketchHandoffRecord(
251
+ // Handoff records are read back from storage and parsed here.
252
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
253
+ value) {
254
+ if (!isPlainObject(value))
199
255
  return false;
256
+ // SAFETY: the object check above makes reading optional candidate fields safe.
200
257
  const handoff = value;
201
258
  return Object.keys(handoff).every((key) => [
202
259
  "id",
@@ -213,14 +270,12 @@ export function isSketchHandoffRecord(value) {
213
270
  && boundedString(handoff.id, 256)
214
271
  && boundedString(handoff.projectId, 256)
215
272
  && boundedString(handoff.sketchId, 256)
216
- && Number.isSafeInteger(handoff.sketchRevision)
217
- && handoff.sketchRevision > 0
273
+ && safeIntegerInRange(handoff.sketchRevision, 1)
218
274
  && boundedString(handoff.localBatchId, 256)
219
- && (handoff.batchRevision === undefined
220
- || (Number.isSafeInteger(handoff.batchRevision) && handoff.batchRevision >= 0))
275
+ && (handoff.batchRevision === undefined || safeIntegerInRange(handoff.batchRevision, 0))
221
276
  && (handoff.transport === "local" || handoff.transport === "clipboard" || handoff.transport === "direct")
222
277
  && (handoff.agentRequestId === undefined || boundedString(handoff.agentRequestId, 256))
223
- && Number.isSafeInteger(handoff.timestamp)
278
+ && safeIntegerInRange(handoff.timestamp, Number.MIN_SAFE_INTEGER)
224
279
  && (handoff.state === "pending"
225
280
  || handoff.state === "dispatching"
226
281
  || handoff.state === "handing-off"
@@ -5,10 +5,24 @@ export declare class SketchStorageError extends Error {
5
5
  cause?: unknown;
6
6
  });
7
7
  }
8
+ export declare const SKETCH_STORAGE_FULL_MESSAGE = "Browser storage is full. Remove saved sketches you no longer need, then try again.";
8
9
  export interface SketchPersistenceSnapshot {
9
10
  readonly documents: SketchDocument[];
10
11
  readonly handoffs: SketchHandoffRecord[];
11
12
  }
13
+ /** One atomic set of puts and deletes against a project's sketch records. */
14
+ export interface SketchStorageChange {
15
+ readonly documents?: readonly SketchDocument[];
16
+ readonly handoffs?: readonly SketchHandoffRecord[];
17
+ readonly deleteDocumentIds?: readonly string[];
18
+ readonly deleteHandoffIds?: readonly string[];
19
+ }
20
+ /** Replaces the page origin's IndexedDB for sketches, for example with extension storage. */
21
+ export interface SketchStorage {
22
+ read(projectId: string): Promise<SketchPersistenceSnapshot>;
23
+ update(projectId: string, change: SketchStorageChange): Promise<void>;
24
+ }
25
+ export declare function configureSketchStorage(storage: SketchStorage | null): void;
12
26
  export declare function readSketchPersistence(projectId: string): Promise<SketchPersistenceSnapshot>;
13
27
  /** Writes a document and its initial/current handoff atomically. */
14
28
  export declare function writeSketchPersistence(projectId: string, document: SketchDocument, handoff: SketchHandoffRecord): Promise<void>;
@@ -1,3 +1,4 @@
1
+ import { isDemoRuntime } from "../runtime/runtimeConfig.js";
1
2
  import { SKETCH_DATABASE_NAME, SKETCH_DATABASE_VERSION, SKETCH_DOCUMENTS_STORE, SKETCH_HANDOFFS_STORE, } from "./model.js";
2
3
  export class SketchStorageError extends Error {
3
4
  code = "sketch_storage_error";
@@ -6,8 +7,30 @@ export class SketchStorageError extends Error {
6
7
  this.name = "SketchStorageError";
7
8
  }
8
9
  }
10
+ export const SKETCH_STORAGE_FULL_MESSAGE = "Browser storage is full. Remove saved sketches you no longer need, then try again.";
11
+ // IndexedDB rejects with a QuotaExceededError; `chrome.storage` with a "quota exceeded" message.
12
+ function isQuotaError(error) {
13
+ if (error instanceof DOMException && error.name === "QuotaExceededError")
14
+ return true;
15
+ return error instanceof Error && /quota/i.test(error.message);
16
+ }
17
+ function storageError(message, error) {
18
+ return new SketchStorageError(isQuotaError(error) ? SKETCH_STORAGE_FULL_MESSAGE : message, { cause: error });
19
+ }
9
20
  const memoryProjects = new Map();
10
21
  let databasePromise = null;
22
+ let configuredStorage = null;
23
+ export function configureSketchStorage(storage) {
24
+ configuredStorage = storage;
25
+ }
26
+ async function updateConfigured(storage, projectId, change, message) {
27
+ try {
28
+ await storage.update(projectId, change);
29
+ }
30
+ catch (error) {
31
+ throw storageError(message, error);
32
+ }
33
+ }
11
34
  function memoryState(projectId) {
12
35
  let state = memoryProjects.get(projectId);
13
36
  if (!state) {
@@ -38,6 +61,10 @@ async function openDatabase() {
38
61
  const factory = indexedDbFactory();
39
62
  if (!factory)
40
63
  return null;
64
+ // Demo runtimes (the landing page) are session-only: sketches live in the
65
+ // in-memory fallback and never touch IndexedDB, so every visit starts clean.
66
+ if (isDemoRuntime())
67
+ return null;
41
68
  if (!databasePromise) {
42
69
  databasePromise = new Promise((resolve, reject) => {
43
70
  let request;
@@ -69,12 +96,20 @@ async function openDatabase() {
69
96
  request.onblocked = () => reject(new Error("IndexedDB upgrade is blocked by another tab."));
70
97
  }).catch((error) => {
71
98
  databasePromise = null;
72
- throw new SketchStorageError("Sketch storage is unavailable in this browser.", { cause: error });
99
+ throw storageError("Sketch storage is unavailable in this browser.", error);
73
100
  });
74
101
  }
75
102
  return databasePromise;
76
103
  }
77
104
  export async function readSketchPersistence(projectId) {
105
+ if (configuredStorage) {
106
+ try {
107
+ return await configuredStorage.read(projectId);
108
+ }
109
+ catch (error) {
110
+ throw storageError("Sketch storage could not be read.", error);
111
+ }
112
+ }
78
113
  const database = await openDatabase();
79
114
  if (!database) {
80
115
  const state = memoryState(projectId);
@@ -92,11 +127,15 @@ export async function readSketchPersistence(projectId) {
92
127
  return { documents, handoffs };
93
128
  }
94
129
  catch (error) {
95
- throw new SketchStorageError("Sketch storage could not be read.", { cause: error });
130
+ throw storageError("Sketch storage could not be read.", error);
96
131
  }
97
132
  }
98
133
  /** Writes a document and its initial/current handoff atomically. */
99
134
  export async function writeSketchPersistence(projectId, document, handoff) {
135
+ if (configuredStorage) {
136
+ const change = { documents: [document], handoffs: [handoff] };
137
+ return updateConfigured(configuredStorage, projectId, change, "Sketch could not be saved to browser storage.");
138
+ }
100
139
  const database = await openDatabase();
101
140
  if (!database) {
102
141
  const state = memoryState(projectId);
@@ -111,10 +150,13 @@ export async function writeSketchPersistence(projectId, document, handoff) {
111
150
  await transactionDone(transaction);
112
151
  }
113
152
  catch (error) {
114
- throw new SketchStorageError("Sketch could not be saved to browser storage.", { cause: error });
153
+ throw storageError("Sketch could not be saved to browser storage.", error);
115
154
  }
116
155
  }
117
156
  export async function writeSketchHandoff(projectId, handoff) {
157
+ if (configuredStorage) {
158
+ return updateConfigured(configuredStorage, projectId, { handoffs: [handoff] }, "Sketch handoff state could not be saved.");
159
+ }
118
160
  const database = await openDatabase();
119
161
  if (!database) {
120
162
  memoryState(projectId).handoffs.set(handoff.id, handoff);
@@ -126,12 +168,15 @@ export async function writeSketchHandoff(projectId, handoff) {
126
168
  await transactionDone(transaction);
127
169
  }
128
170
  catch (error) {
129
- throw new SketchStorageError("Sketch handoff state could not be saved.", { cause: error });
171
+ throw storageError("Sketch handoff state could not be saved.", error);
130
172
  }
131
173
  }
132
174
  export async function writeSketchHandoffs(projectId, handoffs) {
133
175
  if (handoffs.length === 0)
134
176
  return;
177
+ if (configuredStorage) {
178
+ return updateConfigured(configuredStorage, projectId, { handoffs }, "Sketch handoff state could not be saved.");
179
+ }
135
180
  const database = await openDatabase();
136
181
  if (!database) {
137
182
  const state = memoryState(projectId);
@@ -145,7 +190,7 @@ export async function writeSketchHandoffs(projectId, handoffs) {
145
190
  await transactionDone(transaction);
146
191
  }
147
192
  catch (error) {
148
- throw new SketchStorageError("Sketch handoff state could not be saved.", { cause: error });
193
+ throw storageError("Sketch handoff state could not be saved.", error);
149
194
  }
150
195
  }
151
196
  export async function deleteSketchPersistence(projectId, sketchId) {
@@ -153,6 +198,10 @@ export async function deleteSketchPersistence(projectId, sketchId) {
153
198
  const handoffIds = existing.handoffs
154
199
  .filter((handoff) => handoff.sketchId === sketchId)
155
200
  .map((handoff) => handoff.id);
201
+ if (configuredStorage) {
202
+ const change = { deleteDocumentIds: [sketchId], deleteHandoffIds: handoffIds };
203
+ return updateConfigured(configuredStorage, projectId, change, "Sketch could not be removed from browser storage.");
204
+ }
156
205
  const database = await openDatabase();
157
206
  if (!database) {
158
207
  const state = memoryState(projectId);
@@ -168,11 +217,18 @@ export async function deleteSketchPersistence(projectId, sketchId) {
168
217
  await transactionDone(transaction);
169
218
  }
170
219
  catch (error) {
171
- throw new SketchStorageError("Sketch could not be removed from browser storage.", { cause: error });
220
+ throw storageError("Sketch could not be removed from browser storage.", error);
172
221
  }
173
222
  }
174
223
  export async function clearSketchPersistence(projectId) {
175
224
  const existing = await readSketchPersistence(projectId);
225
+ if (configuredStorage) {
226
+ const change = {
227
+ deleteDocumentIds: existing.documents.map((document) => document.id),
228
+ deleteHandoffIds: existing.handoffs.map((handoff) => handoff.id),
229
+ };
230
+ return updateConfigured(configuredStorage, projectId, change, "Sketch storage could not be cleared.");
231
+ }
176
232
  const database = await openDatabase();
177
233
  if (!database) {
178
234
  const state = memoryState(projectId);
@@ -189,7 +245,7 @@ export async function clearSketchPersistence(projectId) {
189
245
  await transactionDone(transaction);
190
246
  }
191
247
  catch (error) {
192
- throw new SketchStorageError("Sketch storage could not be cleared.", { cause: error });
248
+ throw storageError("Sketch storage could not be cleared.", error);
193
249
  }
194
250
  }
195
251
  export async function closeSketchDatabase() {
@@ -10,12 +10,16 @@ export declare function drawSketchStroke(ctx: CanvasRenderingContext2D, stroke:
10
10
  export declare function drawSketchStrokes(ctx: CanvasRenderingContext2D, strokes: readonly SketchStroke[], scaleX?: number, scaleY?: number): void;
11
11
  export declare function drawSketchAnnotation(ctx: CanvasRenderingContext2D, annotation: Pick<SketchAnnotation, "number" | "point">, scaleX?: number, scaleY?: number): void;
12
12
  export declare function drawSketchAnnotations(ctx: CanvasRenderingContext2D, annotations: readonly SketchAnnotation[], scaleX?: number, scaleY?: number): void;
13
- export declare function renderAnnotatedPng(originalImage: Blob, imageWidth: number, imageHeight: number, strokes: readonly SketchStroke[], annotations?: readonly SketchAnnotation[], description?: string): Promise<{
13
+ export declare function renderAnnotatedPng(originalImage: Blob, imageWidth: number, imageHeight: number, strokes: readonly SketchStroke[], annotations?: readonly SketchAnnotation[], description?: string, heading?: string): Promise<{
14
14
  readonly blob: Blob;
15
15
  readonly width: number;
16
16
  readonly height: number;
17
17
  }>;
18
18
  export declare function blobToBase64(blob: Blob): Promise<string>;
19
- export declare function copyImageToClipboard(blob: Blob): Promise<void>;
19
+ /**
20
+ * Accepts a pending blob so callers can start the write inside the user gesture
21
+ * (required by Safari) and resolve the image once rendering finishes.
22
+ */
23
+ export declare function copyImageToClipboard(blob: Blob | Promise<Blob>): Promise<void>;
20
24
  export declare function copyImageAndTextToClipboard(blob: Blob, text: string): Promise<void>;
21
25
  export declare function downloadImage(blob: Blob, filename: string): void;