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,205 @@
1
+ import { SKETCH_DATABASE_NAME, SKETCH_DATABASE_VERSION, SKETCH_DOCUMENTS_STORE, SKETCH_HANDOFFS_STORE, } from "./model.js";
2
+ export class SketchStorageError extends Error {
3
+ code = "sketch_storage_error";
4
+ constructor(message, options) {
5
+ super(message, options);
6
+ this.name = "SketchStorageError";
7
+ }
8
+ }
9
+ const memoryProjects = new Map();
10
+ let databasePromise = null;
11
+ function memoryState(projectId) {
12
+ let state = memoryProjects.get(projectId);
13
+ if (!state) {
14
+ state = { documents: new Map(), handoffs: new Map() };
15
+ memoryProjects.set(projectId, state);
16
+ }
17
+ return state;
18
+ }
19
+ function indexedDbFactory() {
20
+ if (typeof indexedDB === "undefined")
21
+ return null;
22
+ return indexedDB;
23
+ }
24
+ function requestResult(request) {
25
+ return new Promise((resolve, reject) => {
26
+ request.onsuccess = () => resolve(request.result);
27
+ request.onerror = () => reject(request.error ?? new Error("IndexedDB request failed."));
28
+ });
29
+ }
30
+ function transactionDone(transaction) {
31
+ return new Promise((resolve, reject) => {
32
+ transaction.oncomplete = () => resolve();
33
+ transaction.onerror = () => reject(transaction.error ?? new Error("IndexedDB transaction failed."));
34
+ transaction.onabort = () => reject(transaction.error ?? new Error("IndexedDB transaction was aborted."));
35
+ });
36
+ }
37
+ async function openDatabase() {
38
+ const factory = indexedDbFactory();
39
+ if (!factory)
40
+ return null;
41
+ if (!databasePromise) {
42
+ databasePromise = new Promise((resolve, reject) => {
43
+ let request;
44
+ try {
45
+ request = factory.open(SKETCH_DATABASE_NAME, SKETCH_DATABASE_VERSION);
46
+ }
47
+ catch (error) {
48
+ reject(error);
49
+ return;
50
+ }
51
+ request.onupgradeneeded = () => {
52
+ const database = request.result;
53
+ if (!database.objectStoreNames.contains(SKETCH_DOCUMENTS_STORE)) {
54
+ const documents = database.createObjectStore(SKETCH_DOCUMENTS_STORE, { keyPath: "id" });
55
+ documents.createIndex("projectId", "projectId", { unique: false });
56
+ }
57
+ if (!database.objectStoreNames.contains(SKETCH_HANDOFFS_STORE)) {
58
+ const handoffs = database.createObjectStore(SKETCH_HANDOFFS_STORE, { keyPath: "id" });
59
+ handoffs.createIndex("projectId", "projectId", { unique: false });
60
+ handoffs.createIndex("sketchKey", ["sketchId", "sketchRevision"], { unique: false });
61
+ }
62
+ };
63
+ request.onsuccess = () => {
64
+ const database = request.result;
65
+ database.onversionchange = () => database.close();
66
+ resolve(database);
67
+ };
68
+ request.onerror = () => reject(request.error ?? new Error("IndexedDB could not be opened."));
69
+ request.onblocked = () => reject(new Error("IndexedDB upgrade is blocked by another tab."));
70
+ }).catch((error) => {
71
+ databasePromise = null;
72
+ throw new SketchStorageError("Sketch storage is unavailable in this browser.", { cause: error });
73
+ });
74
+ }
75
+ return databasePromise;
76
+ }
77
+ export async function readSketchPersistence(projectId) {
78
+ const database = await openDatabase();
79
+ if (!database) {
80
+ const state = memoryState(projectId);
81
+ return {
82
+ documents: [...state.documents.values()],
83
+ handoffs: [...state.handoffs.values()],
84
+ };
85
+ }
86
+ try {
87
+ const transaction = database.transaction([SKETCH_DOCUMENTS_STORE, SKETCH_HANDOFFS_STORE], "readonly");
88
+ const done = transactionDone(transaction);
89
+ const documentsPromise = requestResult(transaction.objectStore(SKETCH_DOCUMENTS_STORE).index("projectId").getAll(projectId));
90
+ const handoffsPromise = requestResult(transaction.objectStore(SKETCH_HANDOFFS_STORE).index("projectId").getAll(projectId));
91
+ const [documents, handoffs] = await Promise.all([documentsPromise, handoffsPromise, done]);
92
+ return { documents, handoffs };
93
+ }
94
+ catch (error) {
95
+ throw new SketchStorageError("Sketch storage could not be read.", { cause: error });
96
+ }
97
+ }
98
+ /** Writes a document and its initial/current handoff atomically. */
99
+ export async function writeSketchPersistence(projectId, document, handoff) {
100
+ const database = await openDatabase();
101
+ if (!database) {
102
+ const state = memoryState(projectId);
103
+ state.documents.set(document.id, document);
104
+ state.handoffs.set(handoff.id, handoff);
105
+ return;
106
+ }
107
+ try {
108
+ const transaction = database.transaction([SKETCH_DOCUMENTS_STORE, SKETCH_HANDOFFS_STORE], "readwrite");
109
+ transaction.objectStore(SKETCH_DOCUMENTS_STORE).put(document);
110
+ transaction.objectStore(SKETCH_HANDOFFS_STORE).put(handoff);
111
+ await transactionDone(transaction);
112
+ }
113
+ catch (error) {
114
+ throw new SketchStorageError("Sketch could not be saved to browser storage.", { cause: error });
115
+ }
116
+ }
117
+ export async function writeSketchHandoff(projectId, handoff) {
118
+ const database = await openDatabase();
119
+ if (!database) {
120
+ memoryState(projectId).handoffs.set(handoff.id, handoff);
121
+ return;
122
+ }
123
+ try {
124
+ const transaction = database.transaction(SKETCH_HANDOFFS_STORE, "readwrite");
125
+ transaction.objectStore(SKETCH_HANDOFFS_STORE).put(handoff);
126
+ await transactionDone(transaction);
127
+ }
128
+ catch (error) {
129
+ throw new SketchStorageError("Sketch handoff state could not be saved.", { cause: error });
130
+ }
131
+ }
132
+ export async function writeSketchHandoffs(projectId, handoffs) {
133
+ if (handoffs.length === 0)
134
+ return;
135
+ const database = await openDatabase();
136
+ if (!database) {
137
+ const state = memoryState(projectId);
138
+ handoffs.forEach((handoff) => state.handoffs.set(handoff.id, handoff));
139
+ return;
140
+ }
141
+ try {
142
+ const transaction = database.transaction(SKETCH_HANDOFFS_STORE, "readwrite");
143
+ const store = transaction.objectStore(SKETCH_HANDOFFS_STORE);
144
+ handoffs.forEach((handoff) => store.put(handoff));
145
+ await transactionDone(transaction);
146
+ }
147
+ catch (error) {
148
+ throw new SketchStorageError("Sketch handoff state could not be saved.", { cause: error });
149
+ }
150
+ }
151
+ export async function deleteSketchPersistence(projectId, sketchId) {
152
+ const existing = await readSketchPersistence(projectId);
153
+ const handoffIds = existing.handoffs
154
+ .filter((handoff) => handoff.sketchId === sketchId)
155
+ .map((handoff) => handoff.id);
156
+ const database = await openDatabase();
157
+ if (!database) {
158
+ const state = memoryState(projectId);
159
+ state.documents.delete(sketchId);
160
+ handoffIds.forEach((id) => state.handoffs.delete(id));
161
+ return;
162
+ }
163
+ try {
164
+ const transaction = database.transaction([SKETCH_DOCUMENTS_STORE, SKETCH_HANDOFFS_STORE], "readwrite");
165
+ transaction.objectStore(SKETCH_DOCUMENTS_STORE).delete(sketchId);
166
+ const handoffs = transaction.objectStore(SKETCH_HANDOFFS_STORE);
167
+ handoffIds.forEach((id) => handoffs.delete(id));
168
+ await transactionDone(transaction);
169
+ }
170
+ catch (error) {
171
+ throw new SketchStorageError("Sketch could not be removed from browser storage.", { cause: error });
172
+ }
173
+ }
174
+ export async function clearSketchPersistence(projectId) {
175
+ const existing = await readSketchPersistence(projectId);
176
+ const database = await openDatabase();
177
+ if (!database) {
178
+ const state = memoryState(projectId);
179
+ state.documents.clear();
180
+ state.handoffs.clear();
181
+ return;
182
+ }
183
+ try {
184
+ const transaction = database.transaction([SKETCH_DOCUMENTS_STORE, SKETCH_HANDOFFS_STORE], "readwrite");
185
+ const documents = transaction.objectStore(SKETCH_DOCUMENTS_STORE);
186
+ const handoffs = transaction.objectStore(SKETCH_HANDOFFS_STORE);
187
+ existing.documents.forEach((document) => documents.delete(document.id));
188
+ existing.handoffs.forEach((handoff) => handoffs.delete(handoff.id));
189
+ await transactionDone(transaction);
190
+ }
191
+ catch (error) {
192
+ throw new SketchStorageError("Sketch storage could not be cleared.", { cause: error });
193
+ }
194
+ }
195
+ export async function closeSketchDatabase() {
196
+ if (!databasePromise)
197
+ return;
198
+ const database = await databasePromise.catch(() => null);
199
+ database?.close();
200
+ databasePromise = null;
201
+ }
202
+ /** Test seam for the no-IndexedDB fallback; it does not delete browser data. */
203
+ export function resetSketchMemory() {
204
+ memoryProjects.clear();
205
+ }
@@ -0,0 +1,22 @@
1
+ import { type SketchAnnotation, type SketchStroke } from "./model.js";
2
+ export interface PngDimensions {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ }
6
+ export declare function readPngDimensions(bytes: Uint8Array): PngDimensions | null;
7
+ export declare function getPngDimensions(blob: Blob): Promise<PngDimensions | null>;
8
+ export declare function validatePngBlob(blob: Blob, expected?: PngDimensions): Promise<PngDimensions>;
9
+ export declare function drawSketchStroke(ctx: CanvasRenderingContext2D, stroke: Pick<SketchStroke, "points" | "width"> & Partial<Pick<SketchStroke, "kind" | "color">>, scaleX?: number, scaleY?: number): void;
10
+ export declare function drawSketchStrokes(ctx: CanvasRenderingContext2D, strokes: readonly SketchStroke[], scaleX?: number, scaleY?: number): void;
11
+ export declare function drawSketchAnnotation(ctx: CanvasRenderingContext2D, annotation: Pick<SketchAnnotation, "number" | "point">, scaleX?: number, scaleY?: number): void;
12
+ export declare function drawSketchAnnotations(ctx: CanvasRenderingContext2D, annotations: readonly SketchAnnotation[], scaleX?: number, scaleY?: number): void;
13
+ /** Renders the immutable screenshot and committed strokes into one PNG. */
14
+ export declare function renderAnnotatedPng(originalImage: Blob, imageWidth: number, imageHeight: number, strokes: readonly SketchStroke[], annotations?: readonly SketchAnnotation[]): Promise<{
15
+ readonly blob: Blob;
16
+ readonly width: number;
17
+ readonly height: number;
18
+ }>;
19
+ export declare function blobToBase64(blob: Blob): Promise<string>;
20
+ export declare function copyImageToClipboard(blob: Blob): Promise<void>;
21
+ export declare function copyImageAndTextToClipboard(blob: Blob, text: string): Promise<void>;
22
+ export declare function downloadImage(blob: Blob, filename: string): void;
@@ -0,0 +1,203 @@
1
+ import { SKETCH_ANNOTATION_RADIUS, SKETCH_LIMITS, SKETCH_STROKE_COLOR, } from "./model.js";
2
+ import { sketchStrokeOutline } from "./freehand.js";
3
+ const PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
4
+ export function readPngDimensions(bytes) {
5
+ if (bytes.length < 24)
6
+ return null;
7
+ for (let index = 0; index < PNG_SIGNATURE.length; index += 1) {
8
+ if (bytes[index] !== PNG_SIGNATURE[index])
9
+ return null;
10
+ }
11
+ // IHDR is the first chunk in every PNG. The dimensions are big-endian at
12
+ // byte offsets 16 and 20 in the complete file.
13
+ if (bytes[12] !== 73 || bytes[13] !== 72 || bytes[14] !== 68 || bytes[15] !== 82)
14
+ return null;
15
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
16
+ const width = view.getUint32(16);
17
+ const height = view.getUint32(20);
18
+ if (!width || !height)
19
+ return null;
20
+ return { width, height };
21
+ }
22
+ export async function getPngDimensions(blob) {
23
+ return readPngDimensions(new Uint8Array(await blob.slice(0, 24).arrayBuffer()));
24
+ }
25
+ export async function validatePngBlob(blob, expected) {
26
+ if (blob.type !== "image/png" || blob.size <= 0 || blob.size > SKETCH_LIMITS.imageBytes) {
27
+ throw new Error("Sketch images must be PNG files smaller than 2 MiB.");
28
+ }
29
+ const dimensions = await getPngDimensions(blob);
30
+ if (!dimensions
31
+ || dimensions.width > SKETCH_LIMITS.imageEdge
32
+ || dimensions.height > SKETCH_LIMITS.imageEdge) {
33
+ throw new Error("Sketch images must be valid PNGs no larger than 2048 pixels on an edge.");
34
+ }
35
+ if (expected && (expected.width !== dimensions.width || expected.height !== dimensions.height)) {
36
+ throw new Error("The sketch image dimensions do not match its capture metadata.");
37
+ }
38
+ return dimensions;
39
+ }
40
+ function drawPointPath(ctx, points, scaleX, scaleY) {
41
+ const first = points[0];
42
+ if (!first)
43
+ return;
44
+ ctx.beginPath();
45
+ ctx.moveTo(first.x * scaleX, first.y * scaleY);
46
+ for (let index = 1; index < points.length; index += 1) {
47
+ const point = points[index];
48
+ ctx.lineTo(point.x * scaleX, point.y * scaleY);
49
+ }
50
+ if (points.length === 1) {
51
+ ctx.lineTo(first.x * scaleX + 0.01, first.y * scaleY + 0.01);
52
+ }
53
+ }
54
+ export function drawSketchStroke(ctx, stroke, scaleX = 1, scaleY = scaleX) {
55
+ if (stroke.points.length === 0)
56
+ return;
57
+ ctx.save();
58
+ if (stroke.kind === "rectangle") {
59
+ ctx.lineCap = "round";
60
+ ctx.lineJoin = "round";
61
+ ctx.strokeStyle = stroke.color || SKETCH_STROKE_COLOR;
62
+ ctx.lineWidth = stroke.width * Math.max(scaleX, scaleY);
63
+ drawPointPath(ctx, stroke.points, scaleX, scaleY);
64
+ ctx.stroke();
65
+ }
66
+ else {
67
+ const outline = sketchStrokeOutline(stroke, scaleX, scaleY);
68
+ const first = outline[0];
69
+ if (first) {
70
+ ctx.beginPath();
71
+ ctx.moveTo(first[0], first[1]);
72
+ for (const point of outline.slice(1))
73
+ ctx.lineTo(point[0], point[1]);
74
+ ctx.closePath();
75
+ ctx.fillStyle = stroke.color || SKETCH_STROKE_COLOR;
76
+ ctx.fill();
77
+ }
78
+ }
79
+ ctx.restore();
80
+ }
81
+ export function drawSketchStrokes(ctx, strokes, scaleX = 1, scaleY = scaleX) {
82
+ for (const stroke of strokes)
83
+ drawSketchStroke(ctx, stroke, scaleX, scaleY);
84
+ }
85
+ export function drawSketchAnnotation(ctx, annotation, scaleX = 1, scaleY = scaleX) {
86
+ const scale = Math.max(scaleX, scaleY);
87
+ const x = annotation.point.x * scaleX;
88
+ const y = annotation.point.y * scaleY;
89
+ const radius = SKETCH_ANNOTATION_RADIUS * scale;
90
+ ctx.save();
91
+ ctx.fillStyle = SKETCH_STROKE_COLOR;
92
+ ctx.beginPath();
93
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
94
+ ctx.fill();
95
+ ctx.fillStyle = "#ffffff";
96
+ ctx.font = `600 ${12 * scale}px system-ui, sans-serif`;
97
+ ctx.textAlign = "center";
98
+ ctx.textBaseline = "middle";
99
+ ctx.fillText(String(annotation.number), x, y);
100
+ ctx.restore();
101
+ }
102
+ export function drawSketchAnnotations(ctx, annotations, scaleX = 1, scaleY = scaleX) {
103
+ for (const annotation of annotations) {
104
+ drawSketchAnnotation(ctx, annotation, scaleX, scaleY);
105
+ }
106
+ }
107
+ async function loadImage(blob) {
108
+ if (typeof createImageBitmap === "function") {
109
+ const bitmap = await createImageBitmap(blob);
110
+ return { source: bitmap, close: () => bitmap.close() };
111
+ }
112
+ if (typeof Image === "undefined" || typeof URL === "undefined") {
113
+ throw new Error("This browser cannot decode sketch images.");
114
+ }
115
+ const url = URL.createObjectURL(blob);
116
+ try {
117
+ const image = await new Promise((resolve, reject) => {
118
+ const element = new Image();
119
+ element.onload = () => resolve(element);
120
+ element.onerror = () => reject(new Error("The sketch screenshot could not be decoded."));
121
+ element.src = url;
122
+ });
123
+ return { source: image, close: () => URL.revokeObjectURL(url) };
124
+ }
125
+ catch (error) {
126
+ URL.revokeObjectURL(url);
127
+ throw error;
128
+ }
129
+ }
130
+ function canvasPng(canvas) {
131
+ return new Promise((resolve, reject) => {
132
+ canvas.toBlob((blob) => {
133
+ if (blob)
134
+ resolve(blob);
135
+ else
136
+ reject(new Error("The browser could not encode the sketch as PNG."));
137
+ }, "image/png");
138
+ });
139
+ }
140
+ /** Renders the immutable screenshot and committed strokes into one PNG. */
141
+ export async function renderAnnotatedPng(originalImage, imageWidth, imageHeight, strokes, annotations = []) {
142
+ await validatePngBlob(originalImage, { width: imageWidth, height: imageHeight });
143
+ if (typeof document === "undefined")
144
+ throw new Error("Sketch rendering requires a browser document.");
145
+ const image = await loadImage(originalImage);
146
+ try {
147
+ const canvas = document.createElement("canvas");
148
+ canvas.width = imageWidth;
149
+ canvas.height = imageHeight;
150
+ const context = canvas.getContext("2d");
151
+ if (!context)
152
+ throw new Error("The browser could not create a sketch canvas.");
153
+ context.drawImage(image.source, 0, 0, imageWidth, imageHeight);
154
+ drawSketchStrokes(context, strokes);
155
+ drawSketchAnnotations(context, annotations);
156
+ const result = await canvasPng(canvas);
157
+ if (result.size > SKETCH_LIMITS.imageBytes) {
158
+ throw new Error("This sketch is too detailed to save within the 2 MiB image limit.");
159
+ }
160
+ return { blob: result, width: imageWidth, height: imageHeight };
161
+ }
162
+ finally {
163
+ image.close();
164
+ }
165
+ }
166
+ export async function blobToBase64(blob) {
167
+ const bytes = new Uint8Array(await blob.arrayBuffer());
168
+ let binary = "";
169
+ const chunkSize = 0x8000;
170
+ for (let offset = 0; offset < bytes.length; offset += chunkSize) {
171
+ binary += String.fromCharCode(...bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length)));
172
+ }
173
+ if (typeof btoa !== "function")
174
+ throw new Error("This browser cannot prepare sketch attachments.");
175
+ return btoa(binary);
176
+ }
177
+ export async function copyImageToClipboard(blob) {
178
+ if (typeof navigator === "undefined" || !navigator.clipboard?.write || typeof ClipboardItem === "undefined") {
179
+ throw new Error("Image clipboard access is unavailable. Use Download image instead.");
180
+ }
181
+ await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
182
+ }
183
+ export async function copyImageAndTextToClipboard(blob, text) {
184
+ if (typeof navigator === "undefined" || !navigator.clipboard?.write || typeof ClipboardItem === "undefined") {
185
+ throw new Error("Combined image and text clipboard access is unavailable. Copy the prompt and image separately.");
186
+ }
187
+ await navigator.clipboard.write([
188
+ new ClipboardItem({
189
+ "image/png": blob,
190
+ "text/plain": new Blob([text], { type: "text/plain" }),
191
+ }),
192
+ ]);
193
+ }
194
+ export function downloadImage(blob, filename) {
195
+ if (typeof document === "undefined" || typeof URL === "undefined")
196
+ return;
197
+ const url = URL.createObjectURL(blob);
198
+ const link = document.createElement("a");
199
+ link.href = url;
200
+ link.download = filename;
201
+ link.click();
202
+ setTimeout(() => URL.revokeObjectURL(url), 0);
203
+ }
@@ -0,0 +1,10 @@
1
+ interface SketchNoteSnapshot {
2
+ readonly sketchId: string | null;
3
+ }
4
+ export declare function getSketchNoteSnapshot(): SketchNoteSnapshot;
5
+ export declare function subscribeSketchNote(listener: () => void): () => void;
6
+ export declare function useSketchNote(): SketchNoteSnapshot;
7
+ export declare function openSketchNote(sketchId: string): void;
8
+ export declare function closeSketchNote(): void;
9
+ export declare function resetSketchNote(): void;
10
+ export {};
@@ -0,0 +1,30 @@
1
+ import { useSyncExternalStore } from "react";
2
+ const EMPTY_SNAPSHOT = { sketchId: null };
3
+ let snapshot = EMPTY_SNAPSHOT;
4
+ const listeners = new Set();
5
+ function publish(next) {
6
+ snapshot = next;
7
+ for (const listener of listeners)
8
+ listener();
9
+ }
10
+ export function getSketchNoteSnapshot() {
11
+ return snapshot;
12
+ }
13
+ export function subscribeSketchNote(listener) {
14
+ listeners.add(listener);
15
+ return () => listeners.delete(listener);
16
+ }
17
+ export function useSketchNote() {
18
+ return useSyncExternalStore(subscribeSketchNote, getSketchNoteSnapshot, getSketchNoteSnapshot);
19
+ }
20
+ export function openSketchNote(sketchId) {
21
+ publish({ sketchId });
22
+ }
23
+ export function closeSketchNote() {
24
+ if (snapshot.sketchId === null)
25
+ return;
26
+ publish(EMPTY_SNAPSHOT);
27
+ }
28
+ export function resetSketchNote() {
29
+ snapshot = EMPTY_SNAPSHOT;
30
+ }
@@ -0,0 +1,47 @@
1
+ import { toAgentSketchMetadata, type SketchCaptureMetadata, type SketchAnnotation, type SketchDocument, type SketchHandoffRecord, type SketchQueueItem, type SketchStroke } from "./model.js";
2
+ export interface SaveSketchInput {
3
+ readonly id?: string;
4
+ readonly capture: SketchCaptureMetadata;
5
+ readonly description: string;
6
+ readonly strokes: readonly SketchStroke[];
7
+ readonly annotations?: readonly SketchAnnotation[];
8
+ readonly imageWidth: number;
9
+ readonly imageHeight: number;
10
+ readonly originalImage: Blob;
11
+ readonly annotatedImage: Blob;
12
+ }
13
+ export interface SketchStoreSnapshot {
14
+ readonly projectId: string | null;
15
+ readonly ready: boolean;
16
+ readonly documents: readonly SketchDocument[];
17
+ readonly handoffs: readonly SketchHandoffRecord[];
18
+ readonly items: readonly SketchQueueItem[];
19
+ readonly error: string | null;
20
+ }
21
+ export declare function getSketchStoreSnapshot(): SketchStoreSnapshot;
22
+ export declare function subscribeSketchStore(listener: () => void): () => void;
23
+ export declare function useSketchStore(): SketchStoreSnapshot;
24
+ export declare function useSketches(): readonly SketchQueueItem[];
25
+ export declare function getSketches(): readonly SketchQueueItem[];
26
+ export declare function getPendingSketches(): readonly SketchQueueItem[];
27
+ export declare function getSketch(sketchId: string): SketchQueueItem | null;
28
+ export declare function initializeSketchStore(projectId?: string): Promise<void>;
29
+ export declare function saveSketch(input: SaveSketchInput): Promise<SketchDocument>;
30
+ export declare function markSketchesDispatching(entries: readonly {
31
+ readonly id: string;
32
+ readonly revision: number;
33
+ }[], batchRevision: number, localBatchId: string): Promise<boolean>;
34
+ export declare function markSketchesHandingOff(entries: readonly {
35
+ readonly id: string;
36
+ readonly revision: number;
37
+ }[], batchRevision: number, localBatchId: string, agentRequestId?: string): Promise<boolean>;
38
+ export declare function settleSketchDispatch(batchRevision: number | undefined, state: "working" | "completed" | "failed" | "interrupted", agentRequestId?: string, localBatchId?: string): Promise<boolean>;
39
+ export declare function markSketchesHandedOff(entries: readonly {
40
+ readonly id: string;
41
+ readonly revision: number;
42
+ }[], localBatchId?: string): Promise<boolean>;
43
+ export declare function requeueSketch(sketchId: string): Promise<boolean>;
44
+ export declare function removeSketch(sketchId: string): Promise<boolean>;
45
+ export declare function clearSketchesForProject(projectId?: string): Promise<void>;
46
+ export declare function sketchMetadataFor(items: readonly SketchQueueItem[]): ReturnType<typeof toAgentSketchMetadata>[];
47
+ export declare function resetSketchStore(): void;